Skip to content

Instantly share code, notes, and snippets.

@gr2m
Last active October 9, 2019 01:53
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gr2m/e8d3873d120436a966c4918b5d496c7d to your computer and use it in GitHub Desktop.
Save gr2m/e8d3873d120436a966c4918b5d496c7d to your computer and use it in GitHub Desktop.
Living document to make the Octokit JS libraries (https://github.com/octokit?utf8=%E2%9C%93&q=.js&type=&language=) work with ECMAScript Modules, Common JS and modern browsers

My usecase: Universal JavaScript libraries for GitHub's APIs for Node.js and modern browsers.

All libraries are written in Typescript, so a build step is necessary which makes it easier to seperate the source code structure from the distribution code structure. I rely heavily on pika.dev for creating browser bundles and distributing them via their CDN. Example: https://github.com/octokit/request.js/#usage.

I use @pika/pack to create a ./pkg folder with multiple distributions right before publishing to npm. Example: https://unpkg.com/browse/@octokit/request@5.1.0/

Currently, the dist-web/ folder is the ECMAScript Module export, it's referenced using the "module" key in its package.json. The dist-node/ folder is referenced using the "main" key in its package.json.

Besides the differentiation of common JS and ECMAScript Modules, I also need to differentiate between Node & Browser environments. E.g. Node code implementes shims for fetch, navigator.userAgent and Crypto.subtle. The goal is to minimize browser bundle sizes by shiming functionality in Node as needed.

My current idea to support all three is to use the "module" key for Node Code written with ECMAScript Modules, and adding a "browser" for modules that require overrides for browsers.

Conclusion after call with Myles

  • Differentiate between CJS and ESM using explicit entry points.

    1. Default: use new ES Modules: import { Octokit } from 'octokit'
    2. Fallback for Common JS users: const { Octokit } = require('octokit/cjs')

    This will be introduced using a new breaking version for each module.

  • use "exports" to prevent import of private APIs and explicitly export CJS entry point

    {
      "exports": {
        ".": "./dist-esm/index.mjs",
        "./cjs": "./dist-cjs/index.mjs"
      }
    }

Open question: how to support different code for Browsers & Node (at build time)?

This might be out of scope of Node's work on ECMAScript Module support. But I wonder if there have been discussions before about paths for authors of universal JS modules such as universal-user-agent.

I've used the "browser" key in package.json in the past at it seams to be well supported by build tools such as Webpack, Rollup or Browserify.

Unless I misunderstood Myles, the idea of the "exports" map is to also resolve modules to URLs, so that browsers could directly import modules e.g. from https://npmjs.com. What I don't yet understand is how a browser user could do This

import { request } from "https://npmjs.com/@octokit/request"

and the internal module mapping would then import https://npmjs.com/universal-user-agent with the code meant for browsers, not for Node.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment