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

An unknown land of creating libraries

An unknown land of creating libraries

Kamlesh Chandnani

September 15, 2018
Tweet

More Decks by Kamlesh Chandnani

Other Decks in Technology

Transcript

  1. Now, Author Name Slide 1: Title of the talk Slide

    2: Slide 3: : Talk Begins Slide 8: : :
  2. What did you use to make the presentation? Which VSCode

    theme is that? Which font family is that?
  3. What did you use to make the presentation?
 - Keynote

    Which VSCode theme is that?
 - Material Which font family is that?
 - Operator mono
  4. What is a package? A package is a namespace that

    organises set of related files/modules that are responsible of doing some task. In node.js every package has a package.json which has the metadata about that package
  5. What is a module? A module is anything that can

    be loaded with require/import in a Node.js program. import React from 'react'; const webpack = require('webpack');
  6. The simplest answer is we all are lazy and so

    we don’t want to write the same thing over and over again. Hence, we create packages that can be published as modules so that other people can use them directly.
  7. So now since we know the “Why” part of it

    let’s address the “How” part of it
  8. // file1.js const makeMessage = (message) => { return `Message:

    ${message}`; } // file2.js const showMessage = () => { const message = makeMessage('Calling makeMessage’) // makeMessage is undefined console.log(message); }
  9. // file2.js const makeMessage = (message) => { return `Message:

    ${message}`; } const showMessage = () => { const message = makeMessage('Calling makeMessage') console.log(message); }
  10. This was just a small piece of code, but imagine

    if you have big massive application and you’re writing everything in one file.
  11. 1. CommonJS 2. AMD: Async Module Definition 3. UMD: Universal

    Module Definition 4. ECMAScript Harmony (ES6) Various JS Module Systems +
  12. CommonJS Various JS Module Systems + // 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(); • No static analyzing, as you get an object, so property lookup is at runtime. • You always get a copy of an object, so you won’t get live changes from the module itself. • No tree shaking, because when you import you get an object.
  13. CommonJS Various JS Module Systems + //------ lib.cjs.js ------ let

    counter = 3; const incCounter = () => { counter++; } module.exports = { counter, incCounter } //------ main.js ------ const lib = require('./lib.cjs'); // The imported value `counter` is not live console.log(lib.counter); // 3 lib.incCounter(); console.log(lib.counter); // 3
  14. CommonJS Various JS Module Systems + // 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(); • No static analyzing, as you get an object, so property lookup is at runtime. • You always get a copy of an object, so you won’t get live changes from the module itself. • No tree shaking, because when you import you get an object.
  15. AMD: Async Module Definition Various JS Module Systems + //

    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(); }); • Implemented by RequireJs • Complex Syntax • Used for dynamic loading of modules
  16. UMD: Universal Module Definition Various JS Module Systems + //

    File log.js (function (global, factory) { if (typeof define === "function" && define.amd) { define(["exports"], factory); } else if (typeof exports !== "undefined") { factory(exports); } else { var mod = { exports: {} }; factory(mod.exports); global.log = mod.exports; } })(this, function (exports) { "use strict"; function log() { console.log("Example of UMD module system"); } // expose log to other modules exports.log = log; }); • Combination of CommonJs + AMD (that is, Syntax of CommonJs + async loading of AMD)
  17. ECMAScript Harmony (ES6) Various JS Module Systems + // File

    log.js const log = () => { console.log('Example of ES module system'); } export default log // File index.js import log from "./log" log(); • Much cleaner. • Import via “import” and export via “export” • Static analyzing — You can determine imports and exports at compile time (statically) . This opens up doors for Tree Shaking. • When you import, you get back actual value, so any change is a live changes in the module itself.
  18. ECMAScript Harmony (ES6) Various JS Module Systems + //------ lib.es.js

    ------ export default class Counter { static counter = 3; } export const showCounter = () => { console.log('counter', Counter.counter); } //------ main.js ------ import Counter, { showCounter } from './lib'; // The `Counter.counter` is live console.log(showCounter()) // 3 Counter.counter++ console.log(showCounter()) // 4
  19. All of these module systems have one thing in common:

    they allow you to import and export stuff Various JS Module Systems +
  20. Various JS Module Systems + Target 500 million internet users

    in India ~ 52.4% users are on 3G and slower networks
  21. export default class ecmascript { showMessage = () => {

    console.log('Welcome to the world of ES'); }; } ES RAW
  22. export default class ecmascript { constructor() { this.showMessage = ()

    => { console.log('Welcome to the world of ES'); }; } } ES (“modules”: false)
  23. export default class ecmascript { constructor() { this.showMessage = ()

    => { console.log('Welcome to the world of ES'); }; } } ES CJS exports.__esModule = true; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function”); } } var ecmascript = function ecmascript() { _classCallCheck(this, ecmascript); this.showMessage = function () { console.log('Welcome to the world of ES'); }; }; exports.default = ecmascript; (“modules”: false) (“modules”: “commonjs”)
  24. export default class ecmascript { constructor() { this.showMessage = ()

    => { console.log('Welcome to the world of ES'); }; } } ES CJS exports.__esModule = true; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function”); } } var ecmascript = function ecmascript() { _classCallCheck(this, ecmascript); this.showMessage = function () { console.log('Welcome to the world of ES'); }; }; exports.default = ecmascript; # Type Size (Bytes) 1 RAW 112 2 ES 146 3 CJS 398 (“modules”: false) (“modules”: “commonjs”)
  25. Considering the fact that you’re building your code as ES

    target but still what about unused code?
  26. Tree Shaking •It is a term commonly used in the

    context of JavaScript for live code inclusion(import whatever is required). "
 •It relies on the static structure of ES module syntax, that is, “import” and “export”.
  27. Tree Shakeable ✅ // File shakebake.js const shake = ()

    => console.log('shake'); const bake = () => console.log('bake'); //can be tree shaken as we export as es modules export { shake, bake }; // File index.js import { shake } from './shakebake.js' // only "shake" is included in the output
  28. Tree Shakeable ✅ // File shakebake.js const shake = ()

    => console.log('shake'); const bake = () => console.log('bake'); //can be tree shaken as we export as es modules export { shake, bake }; // File index.js import { shake } from './shakebake.js' // only "shake" is included in the output Non Tree Shakeable ❌ // File shakebake.js const shake = () => console.log('shake'); const bake = () => console.log('bake'); //cannot be tree shaken as we have exported an object export default { shake, bake }; // File index.js import { shake } from './shakebake.js' // both "shake" and "bake" are included in the output
  29. Various JS Module Systems + CommonJS ESM Target Comparison #

    CommonJS ESM Tree Shaking ❌ ✅ Async Module Loading ❌ ✅ Static/Compile- time analyzing ❌ ✅ Import/require gives actual value of the module? ❌ ✅
  30. Various JS Module Systems + CommonJS ESM Target There’s more

    in ESM! • <script type=“module”> ➡ deferred by default.
 • Even if we add the <script type=“module”/> multiple times on the page it gets executed just once.
 • Global variables is global to that module (lexical scope) and not to the window scope.
 • You can optimize the delivery of your modules further by using <link rel=“modulepreload”> PS: This is still anexperimental feature.
  31. <html> <head> <script type="module" src="//unpkg.com/something.mjs"></script> <script type="module" src="//unpkg.com/something.mjs"></script> <script type="module"

    src="//unpkg.com/something.mjs"></script> <script type="module" src="//unpkg.com/something.mjs"></script> <!-- The above gets executed just once --> <script src="//unpkg.com/something.js"></script> <script src="//unpkg.com/something.js"></script> <script src="//unpkg.com/something.js"></script> <!-- The above gets executed thrice --> </head> </html>
  32. Various JS Module Systems + CommonJS ESM Target What Else

    in ESM? • <script type=“module”> ➡ deferred by default.
 • Even if we add the <script type=“module”/> multiple times on the page it gets executed just once.
 • Global variables are global to that module (lexical scope) and not to the window scope.
 • You can optimize the delivery of your modules further by using <link rel=“modulepreload”> PS: This is still anexperimental feature.
  33. <html> <head> <script type="module"> const testModule = "This is a

    module" </script> <script type="module"> console.log({ testModule }) // testModule is undefined </script> </head> </html> Scope
  34. Various JS Module Systems + CommonJS ESM Target What Else

    in ESM? • <script type=“module”> ➡ deferred by default.
 • Even if we add the <script type=“module”/> multiple times on the page it gets executed just once.
 • Global variables are global to that module (lexical scope) and not to the window scope.
 • You can optimize the delivery of your modules further by using <link rel=“modulepreload”> PS: This is still anexperimental feature.
  35. Various JS Module Systems + CommonJS ESM Target Advice: CJS/ESM?

    ESM should be targeted primarily and then if there’s a use case for CJS then go for it as the secondary target.
  36. Shall we just compile or bundle the source? Various JS

    Module Systems CommonJS Target Bundle/ Compile ESM +
  37. Compiling: It is a process of transforming the source from

    one module system to another as per the language specs.
 Tools: Babel PS: Babel is a compiler and not transpiler Bundling: It is a technique most commonly used in today’s “module-based” development where common functionalities can be extracted into a flat bundle at build time. Tools: Webpack/Rollup Various JS Module Systems CommonJS Target Bundle/ Compile ESM +
  38. Various JS Module Systems CommonJS Target Bundle/ Compile ESM +

    Component Library: material-ui, blueprint, semantic-ui etc. Core Package: react, react-dom etc. Core Packages Component Library
  39. A good example is package.json which contains two entry point

    versions: “main” and “module” 
 { "name": "ui-kit", "version": "0.0.1", "main": "dist/index.js", "module": "dist/index.mjs", }
  40. “main” is the standard field used by node.js ecosystem and

    therefore it should point to a CJS file.
 “module” field, on the other hand, points to an ES module file.
  41. Component Library 1. Consumer Application should be able to include

    the specific components in your application. import { Button } from 'ui-kit' module: "dist/index.mjs" // ---> The module field in package.json
  42. Component Library 1. Consumer Application should be able to include

    the specific components in your application. import { Button } from 'ui-kit' module: "dist/index.mjs" // ---> The module field in package.json 2. if the consumer application use legacy bundling tools, the author should add support for CJS module system as well. import { Button } from 'ui-kit' main: "dist/index.js" // ---> The main field in package.json
  43. Component Library 1. Consumer Application should be able to include

    the specific components in your application. import { Button } from 'ui-kit' module: "dist/index.mjs" // ---> The module field in package.json 2. if the consumer application use legacy bundling tools, the author should add support for CJS module system as well. import { Button } from 'ui-kit' main: "dist/index.js" // ---> The main field in package.json 3. If the consumer doesn't have ^webpack-4 then no sideEffects benefit. import Button from 'ui-kit/es/Button' import Button from 'ui-kit/cjs/Button'
  44. Component Library 1. Consumer Application should be able to include

    the specific components in your application. import { Button } from 'ui-kit' module: "dist/index.mjs" // ---> The module field in package.json 2. if the consumer application use legacy bundling tools, the author should add support for CJS module system as well. import { Button } from 'ui-kit' main: "dist/index.js" // ---> The main field in package.json 3. If the consumer doesn't have ^webpack-4 then no sideEffects benefit. import Button from 'ui-kit/es/Button' import Button from 'ui-kit/cjs/Button'
 4. Consumer application should be able to directly consume the package i.e 'ui-kit' with <script type="module"> i.e no tree shaking, no side effects in the browser. import Button from "https://unpkg.com/ui-kit/es/Button/index.mjs"
  45. Core Packages 1. Consumer Application should be able to include

    the default and named imports in the application. import api, { get, post } from 'api' module: "dist/index.mjs" // ---> The module field in package.json
  46. Core Packages 1. Consumer Application should be able to include

    the default and named imports in the application. import api, { get, post } from 'api' module: "dist/index.mjs" // ---> The module field in package.json 2. if the consumer application use legacy bundling tools, the author should add support for CJS module system as well. import api, { get, post } from 'api' main: "dist/index.js" // ---> The main field in package.json
  47. Core Packages 1. Consumer Application should be able to include

    the default and named imports in the application. import api, { get, post } from 'api' module: "dist/index.mjs" // ---> The module field in package.json 2. if the consumer application use legacy bundling tools, the author should add support for CJS module system as well. import api, { get, post } from 'api' main: "dist/index.js" // ---> The main field in package.json 3. Consumer application should be able to directly consume the package i.e 'ui-kit' with <script type="module"> i.e no tree shaking, no side effects in the browser. import api, { get, post } from “https://unpkg.com/api/dist/index.mjs” import api, { get, post } from “https://unpkg.com/api/dist/index.js”
  48. Component Library ui-kit dist index.js index.mjs cjs Button Text es

    Button Text api dist index.js index.mjs Core Packages
  49. Various JS Module Systems Core Packages CommonJS Target Bundle/ Compile

    ESM Component Library + Webpack “sideEffects” Flag
  50. Various JS Module Systems Core Packages CommonJS Target Bundle/ Compile

    ESM + Advice: Bundle/Compile? Component Library: 1. Babelify the source and target CJS and ES. 2. Create “index.mjs” with re-exports. Core Packages: 1. Bundle the source with Rollup and target CJS and ES. Component Library
  51. How to Publish? Various JS Module Systems Core Packages Target

    Bundle/ Compile + CommonJS ESM Publish Component Library
  52. Various JS Module Systems Core Packages Target Bundle/ Compile +

    CommonJS ESM Publish NPM publish It’s as simple as that? > npm version patch -m "Upgrade to %s for reasons” > npm publish Component Library
  53. Various JS Module Systems Core Packages Target Bundle/ Compile Component

    Library + CommonJS ESM Publish NPM publish Hmm, so what’s new in that?
  54. 
 Everyone was tired of checking whether the object was

    null over and over. So one of you implemented “isEmpty” and thought to publish the package as npm module.

  55. Now someone from team identified that this “isEmpty” doesn’t checks

    for empty arrays. So they want to make changes to this package. After making changes they publish a minor version of the module to npm. > npm version minor -m "Upgrade to %s for reasons” > npm publish
  56. The problem comes if someone wants to use this, they

    need to update the package version to the latest. “0.30.0” ➡ “0.30.1”
  57. What if you were a large team and there was

    some critical bugfix in one of the package and you want people to upgrade the package version to the latest at any cost? +7+77……+7
  58. What if you were a large team and there was

    some critical bugfix in one of the package and you want people to upgrade the package version to the latest at any cost?
  59. ui-kit dist src package.json api dist src package.json my-app build

    src package.json Repo1 Repo2 Repo3 ui-kit dist src package.json api dist src package.json my-app build src package.json package.json Repo
  60. Left Hand Side It becomes cumbersome if we want to

    refer(symlink) “ui-kit “ and “api” inside “my-app”.
  61. ui-kit dist src package.json api dist src package.json my-app build

    src package.json Repo1 Repo2 Repo3 ui-kit dist src package.json api dist src package.json my-app build src package.json package.json Repo
  62. Right Hand Side It becomes super easy if we want

    to refer(symlink) “ui-kit “ and “api” inside “my-app”, because they are now within same repo and under one hierarchy
  63. ui-kit dist src package.json api dist src package.json my-app build

    src package.json Repo1 Repo2 Repo3 ui-kit dist src package.json api dist src package.json my-app build src package.json package.json Repo
  64. Monorepos? Various JS Module Systems Core Package Target Bundle/ Compile

    Component Library + CommonJS ESM Publish Monorepos NPM publish
  65. Lerna is a tool that optimizes our workflow around managing

    npm packages. It is created by awesome people who built babel.
  66. You can perform operations like build multiple packages, clean multiple

    packages, publish multiple packages for all the packages in your monorepo with simple commands.
  67. Lerna creates symlinks between the packages that refer each other

    inside your monorepo. This process is called “bootstrapping”.

  68. + Publish Monorepos NPM publish Various JS Module Systems Target

    CommonJS ESM Core Package Component Library Bundle/ Compile Advice: npm publish/monorepos? Well, If you’re publishing something to be used globally you have to publish it to npm. If you’re building something that is private within your team/org give monorepos a try.
  69. Various JS Module Systems + Let’s Reiterate Target CommonJS ESM

    Core Package Component Library Bundle/ Compile
  70. + Publish Monorepos NPM publish Let’s Reiterate Various JS Module

    Systems Target CommonJS ESM Core Package Component Library Bundle/ Compile