Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Anatomy of JS module systems and building libraries!!

Anatomy of JS module systems and building libraries!!

What, hows and best practices while building your own library and what are different module systems available that we should target

Kamlesh Chandnani

December 09, 2017
Tweet

More Decks by Kamlesh Chandnani

Other Decks in Technology

Transcript

  1. Kamlesh Chandnani
    Senior Engineer : Mobisy Technologies
    : @_kamlesh_
    : kamleshchandnani
    : @_kamlesh_

    View Slide

  2. Anatomy of JS module systems
    and building libraries!!

    View Slide

  3. 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.

    View Slide

  4. JS module Systems?

    View Slide

  5. View Slide

  6. Why understanding JS Module
    System is important?

    View Slide

  7. Story Time!!

    View Slide

  8. Design & Architect

    View Slide

  9. Common
    functionalities

    View Slide

  10. Copy & Paste

    View Slide

  11. Out of sync,
    Inconsistency,
    manual sync etc.

    View Slide

  12. Extract common
    functionalities into
    an npm package

    View Slide

  13. Sync, Consistency,
    “npm update”

    View Slide

  14. Source directory
    structure

    View Slide

  15. Publish the Library?

    View Slide

  16. 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?

    View Slide

  17. Different Types of JS Module
    Systems

    View Slide

  18. // 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.

    View Slide

  19. // File log.js
    define(['logModule'], function(){
    // export (expose) foo for other modules
    return {
    log: function(){
    console.log('Example of AMD module
    system');
    }
    };
    });
    // File index.js
    require(['log'], function (logModule) {
    logModule.log();
    });
    AMD Modules
    ● Implemented by RequireJs.
    ● Used for client side(browser) when you
    want dynamic loading of modules.
    ● import via “require”.
    ● Complex Syntax.

    View Slide

  20. // 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.

    View Slide

  21. // 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

    View Slide

  22. Let’ Hack on!

    View Slide

  23. UI Library

    View Slide

  24. Best practices before publishing?

    View Slide

  25. 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 };

    View Slide

  26. Publish all module
    variants
    // package.json
    {
    "name": "js-module-system",
    "version": "0.0.1",
    ...
    "main": "dist/index.js",
    "module": "dist/index.es.js",
    ...
    }

    View Slide

  27. 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

    View Slide

  28. Did you know?

    View Slide

  29. Perf Tip: Publish ES version of
    library
    “Ship less, load faster”

    View Slide

  30. What’s next?
    Transpilation or
    Bundling?
    What tools to use?

    View Slide

  31. Webpack vs Rollup vs Babel?

    View Slide

  32. Rollup for libraries
    Webpack for apps
    Babel for
    transpilation

    View Slide

  33. Transpile(Babelify) the source or
    Bundle it?

    View Slide

  34. View Slide

  35. 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.

    View Slide

  36. Mystery is being solved!!

    View Slide

  37. Well, not really!
    Why there’s a difference in build output of UI
    libraries and Core Packages??

    View Slide

  38. <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/>

    View Slide

  39. // 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.

    View Slide

  40. Transpile(Babelify) the source or
    Bundle it?

    View Slide

  41. 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
    ...
    }

    View Slide

  42. 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.

    View Slide

  43. How to build?

    View Slide

  44. // package.json
    {
    ...
    "scripts": {
    "clean": "rimraf dist",
    "build": "run-s clean && run-p build:es build:cjs build:lib:es"
    ,
    "build:es": "NODE_ENV=es rollup -c"
    ,
    "build:cjs": "NODE_ENV=cjs rollup -c"
    ,
    "build:lib:es": "BABEL_ENV=es babel src -d lib"
    }
    ...
    }

    View Slide

  45. // package.json
    {
    ...
    "scripts": {
    "clean": "rimraf dist",
    "build": "run-s clean && run-p build:es build:cjs build:lib:es"
    ,
    "build:es": "NODE_ENV=es rollup -c"
    ,
    "build:cjs": "NODE_ENV=cjs rollup -c"
    ,
    "build:lib:es": "BABEL_ENV=es babel src -d lib"
    }
    ...
    }

    View Slide

  46. // package.json
    {
    ...
    "scripts": {
    "clean": "rimraf dist",
    "build": "run-s clean && run-p build:es build:cjs build:lib:es"
    ,
    "build:es": "NODE_ENV=es rollup -c"
    ,
    "build:cjs": "NODE_ENV=cjs rollup -c"
    ,
    "build:lib:es": "BABEL_ENV=es babel src -d lib"
    }
    ...
    }

    View Slide

  47. // package.json
    {
    ...
    "scripts": {
    "clean": "rimraf dist",
    "build": "run-s clean && run-p build:es build:cjs build:lib:es"
    ,
    "build:es": "NODE_ENV=es rollup -c"
    ,
    "build:cjs": "NODE_ENV=cjs rollup -c"
    ,
    "build:lib:es": "BABEL_ENV=es babel src -d lib"
    }
    ...
    }

    View Slide

  48. Build Output

    View Slide

  49. Publish?
    // package.json
    {
    ...
    "files": ["dist", "lib"]
    ...
    }

    View Slide

  50. View Slide

  51. 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.

    View Slide

  52. /kamleshchandnani/js-module-system
    Slides: bit.ly/js-module-systems

    View Slide

  53. Like it! Share it! Spread it!
    bit.ly/anatomy-js-modules

    View Slide