Skip to content

Instantly share code, notes, and snippets.

@th3hunt
Forked from aelbore/esm-cjs-modules.md
Created April 5, 2022 12:24
Show Gist options
  • Save th3hunt/992913240005d03d399b05632e82af25 to your computer and use it in GitHub Desktop.
Save th3hunt/992913240005d03d399b05632e82af25 to your computer and use it in GitHub Desktop.
Publish your npm package as ES Module, and backward compatibility CommonJS

Create your library

  • Initialize project npm init -y
  • Create esm module ./src/esm/my-lib.js
    function addNumber(value, value2) {
      return value + value2;
    }
    
    export { addNumber };
  • Create commonjs module ./src/cjs/my-lib.js
    function addNumber(value, value2) {
      return value + value2;
    }
    exports.addNumber = addNumber
  • Create ./src/cjs/package.json for commonjs backward compatibility
    {
      "type": "commonjs"
    }
  • Create library package.json ./src/package.json
    {
      "name": "esm-package",
      "type": "module",
      "main": "./cjs/my-lib.js",
      "module": "./esm/my-lib.js",
      "exports": {
        ".": {
          "require":  "./cjs/my-lib.js",
          "default": "./esm/my-lib.js"
        }
      },
      "typings": "my-lib.d.ts"
    }

How to use the package

  • Use the package as esm module
    • update your package.json file (add the following)
      ...
      "type": "module",
      ...
    • create index.js
    import { addNumber } from 'my-lib'
    
    console.log(addNumber(2,3))
  • Use the package as commonjs module
    • update your package.json file (add the following)
      ...
      "type": "commonjs",
      ...
    • create index.js
    const { addNumber } = require('my-lib')
    
    console.log(addNumber(2,3))

Using rollup to bundle your package

  • package.json
    {
      "name": "my-lib",
      "version": "1.0.0",
      "type": "module",
      "typings": "my-lib.d.ts",
      "main": "./cjs/my-lib.js",
      "module": "./esm/my-lib.js",
      "exports": {
        ".": {
          "require":  "./cjs/my-lib.js",
          "default": "./esm/my-lib.js"
        }
      },
      "scripts": {
        "build": "rollup -c"
      },
      "devDependencies": {
        "rollup": "^2.10.5",
        "rollup-plugin-copy": "^3.3.0",
        "rollup-plugin-dts": "^1.4.6",
        "typescript": "^3.9.3"
      }
    }
  • rollup.config.js
    import dts from 'rollup-plugin-dts'
    import copy from 'rollup-plugin-copy'
    
    import { writeFile, mkdir } from 'fs/promises'
    
    function createCommonJsPackage() {
      const pkg = { type: 'commonjs' }
      return {
        name: 'cjs-package',
        buildEnd: async () => {
          await mkdir('./dist/cjs', { recursive: true })
          await writeFile('./dist/cjs/package.json', JSON.stringify(pkg, null, 2))
        }
      }
    }
    
    export default [
      {
        input: './src/my-lib.js',
        plugins: [
          copy({
            targets: [
              { src: './package.json', dest: 'dist' }
            ]
          }),
          createCommonJsPackage()
        ],
        output: [
          { format: 'es', file: './dist/esm/my-lib.js' },
          { format: 'cjs', file: './dist/cjs/my-lib.js' }
        ]
      },
      {
        input: './src/my-lib.js',
        plugins: [ dts() ],
        output: {
          format: 'es',
          file: './dist/my-lib.d.ts'
        }
      }
    ]

Use ts-node

  • As of now ts-node with esm still in "Experimental"
  • Please see https://github.com/TypeStrong/ts-node and TypeStrong/ts-node#1007
  • add scripts to your package.json and update type to module
    {
      "type": "module",
      "scripts": {
        "run:tsnode": "node --no-warnings --loader ts-node/esm.mjs index.ts"
      }
    }
  • tsconfig.json file
    {
      "compilerOptions": {
        "moduleResolution": "node",
        "module": "esnext",
        "target": "es2018"
      }
    }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment