-
Notifications
You must be signed in to change notification settings - Fork 16
Research: ES Modules vs CommonJS
Matěj Chalk edited this page Aug 21, 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 (
.jsor.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
.cjsand.mjsfiles along with appropriatepackage.jsonmetadata (main,module,exports)-
@nx/rollupuses.cjs.jsand.mjs.jsextensions, workaround via customrollup.config.jsandpatch-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
testtarget depends onbuildtargets (automated in Nx task dependencies)
- automatically create symlinks in
-
import.meta.urlused 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
- problematic when importing