Warning This talk contains real life stories, weird codes, crazy questions that some listeners might find annoying. Overall it’ll be a wild ride.! PS: Don’t hit the speaker after the talk.
1. How to make it tree shakeable? 2. What JS module systems shall I target (commonjs, amd, harmony). 3. Shall I transpile the source? 4. Shall I bundle the source? 5. What files to publish?
// File log.js function log(){ console.log('Example of CJS module system'); } // expose log to other modules module.exports = { log } // File index.js var logModule = require('./log'); logModule.log(); CommonJS Modules ● Implemented by node. ● Used for server side when you have modules installed. ● No runtime/async module loading. ● import via “require”. ● export via “module.exports”. ● When you import you get back an object. ● No tree shaking because when you import you get an object.
// File index.js (function (global, factory) { if (typeof define === "function" && define.amd) { define(["./log"], factory); } else if (typeof exports !== "undefined") { factory(require("./log")); } else { var mod = { exports: {} }; factory(global.log); global.index = mod.exports; } })(this, function (logModule) { "use strict"; UMD Modules ● Combination of CommonJs + AMD i.e Syntax of CommonJs + async loading of AMD. ● Can be used for both AMD/CommonJs environments. ● UMD essentially creates a way to use either of the two, while also supporting the global variable definition. As a result, UMD modules are capable of working on both client and server.
// File log.js const log = () => { console.log('Example of ES module system'); } export default log // File index.js import log from "./log" log(); ECMAScript Harmony (ES6) ● Used for both server/client side. ● Runtime/static loading of modules supported ● import via “import” and export via “export” ● Static analyzing — You can determine imports and exports at compile time (statically) ● Tree shakeable because of static analyzing supported by ES6
Tree Shaking const shake = () => console.log('shake'); const bake = () => console.log('bake'); //can be tree shaken as we export as es modules export { shake, bake }; //cannot be tree shaken as we have exported an object export default { shake, bake };
Publish ES6 Version? “js:next”, “js:main” fields in package.json? ( Nah!! ) “module” field in package.json is standardized and used by tools for lookup
1. UI Libraries (styled-components, material-ui) ● There is a dist folder that has the bundled and minified version for ES and UMD/CJS module system as a target. ● There is a lib folder that has the transpiled version of the library. 2. Core Packages (react, react-dom) ● There is just one folder which has bundled and minified version for CJS or UMD module system as a target.
<br/>import {Button} from<br/>"https://unpkg.com/uilibrary/lib/but<br/>ton.js";<br/> Now if we simply transpile the src into lib and host the lib on a CDN, our consumers can actually get whatever they want without any overhead. “Ship less, load faster”. ✅ UI Libraries In browser there is no bundler which will take care of tree shaking and we’ll end up shipping the whole library code to our consumer. ❌ <br/>import {Button} from<br/>"https://unpkg.com/uilibrary/index.<br/>js";<br/>
// CJS require const Button = require("uilibrary/button"); // ES import import {Button} from "uilibrary"; Core Packages Core packages are never utilized via tag, as they need to be part of main application, hence we can safely release the bundled version (UMD, ES) for these kind of packages and leave up to the consumers build system. Example they can use the UMD variant but no tree shaking or they can use the ES variant if the bundler is capable to identify and get the benefits of tree shaking.
UI Library 1. Transpile the source with babel with es module system as a target, place it in “lib”. We can even host the “lib” on a CDN. 2. Bundle and minify the source using rollup, for cjs/umd module system and es module system as a target. Modify the package.json to point to the proper target systems. // package.json { "name": "js-module-system", "version": "0.0.1", ... "main": "dist/index.js", // for umd/cjs builds "module": "dist/index.es.js", // for es build ... }
Core Packages 1. We don’t need the lib version in this case. 2. Bundle and minify the source using rollup, for cjs/umd module system and es module system as a target. Modify the package.json to point to the proper target systems same as above. Tip: We can host the dist folder on CDN as well, for the consumers who are willing to download the whole library/package via tag.
Wrap up! 1. Make it Tree Shakeable. ✅ 2. Target at least ES Harmony and CJS module systems. 3. Use Babel and Bundlers for libraries. 4. Use Bundlers for Core packages. 5. Set module field of package.json to point to the ES version of your module (PS: It helps in tree shaking). 6. Publish the folders which has transpiled as well as bundled versions of you module.