-
Notifications
You must be signed in to change notification settings - Fork 14
Research: ES Modules vs CommonJS
Matěj Chalk edited this page Aug 22, 2023
·
12 revisions
We've decided on using JavaScript config files. But this presents the problem whether to support both CommonJS and ES Modules, and how that can be implemented and tested.
CommonJS | ES Modules | |
---|---|---|
ecosystem trends | older and more widely supported | Node 13+ and some newer packages |
import/export syntax |
require() and module.exports =
|
import and export , also dynamic import()
|
explicit file extensions |
.cjs /.cts
|
.mjs /.mts
|
implicit file extensions (.js ) |
if "type" not specified or set to "commonjs" in package.json
|
if "type": "module" in package.json
|
package.json 's "exports" (separate entry points) |
"require": "<path/to/cjs/script>" |
"import": "<path/to/esm/script>" |
interoperability | ESM ➡️ CJS: must use async import()
|
CJS ➡️ ESM: import ... from './foo.cjs'
|
- CommonJS (cjs) and Modules (esm): Import compatibility
- TypeScript: ECMAScript Modules in Node.js
- Jest: ECMAScript Modules
- some bundlers support multiple formats (tsup, Rollup)
- Nx has a Rollup plugin (@nx/rollup)
- Jest runs as CommonJS, support for ESM experimental
- Vitest runs as ESM
See esm-vs-cjs branch.
- CLI is ESM-only
- ESLint plugin imports
eslint
(CommonJS) and is both ESM and CJS - Lighthouse plugin imports
lighthouse
(ESM) and is therefore ESM-only - config files can use ESM (
.js
or.mjs
), CJS (.cjs
) or TypeScript (.ts
), but CJS is only possible if no ESM-only plugins are included (e.g. Lighthouse) - loading config files can be tested
- depends on build step
- config files may include imports from linked plugins via NPM package name
- config file names are resolved relative to working directory
- Rollup is used to create both
.cjs
and.mjs
files along with appropriatepackage.json
metadata (main
,module
,exports
)-
@nx/rollup
uses.cjs.js
and.mjs.js
extensions, workaround via customrollup.config.js
andpatch-package
-
- Vitest used for testing, since unlike Jest it uses ESM
- imports in JS configs (e.g.
import eslintPlugin from '@quality-metrics/eslint-plugin'
) enabled via NPM workspaces- automatically create symlinks in
node_modules/
fordist/packages/*
onnpm install
- this is why
test
target depends onbuild
targets (automated with Nx task dependencies)
- automatically create symlinks in
-
import.meta.url
used to resolve config files relative to CWD -
Jiti used to import TypeScript files
- problematic when importing
lighthouse
, because Jiti executes in CommonJS - hackfixed via custom Babel plugin (plugin handbook and AST Explorer were incredibly useful, BTW)
- problematic when importing