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/[email protected]/
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.
-
Differentiate between CJS and ESM using explicit entry points.
- Default: use new ES Modules:
import { Octokit } from 'octokit'
- Fallback for Common JS users:
const { Octokit } = require('octokit/cjs')
This will be introduced using a new breaking version for each module.
- Default: use new ES Modules:
-
use
"exports"
to prevent import of private APIs and explicitly export CJS entry point{ "exports": { ".": "./dist-esm/index.mjs", "./cjs": "./dist-cjs/index.mjs" } }
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.