CommonJS vs ESM

JavaScript 🟡 Mid

Définition

CommonJS (require/module.exports) est synchrone et historique dans Node.js ; ESM (import/export) est le standard du langage, statique et propice au tree shaking. Les deux mondes communiquent désormais : require() d'un module ESM synchrone est activé par défaut depuis Node 22.12.0 et 20.19.0, et déclaré stable en Node 25.4.0. Un module avec top-level await reste inaccessible à require() (ERR_REQUIRE_ASYNC_MODULE).

Analogie

CommonJS = commander par téléphone (dynamique, a la demande). ESM = liste de courses pre-écrite (statique, analysable a l'avance).

Exemple de code

// lib.mjs (ESM)
export const lire = (p) => p;

// app.cjs (CommonJS) — Node ≥ 22.12 / 20.19
const { lire } = require('./lib.mjs'); // OK si pas de top-level await
if (process.features.require_module) {
  console.log('require(esm) disponible');
}

Cas d'usage

ESM pour tout nouveau code ; publier en ESM seul devient viable puisque les consommateurs CommonJS peuvent faire require(esm). TypeScript 5.8 modélise ce comportement avec --module nodenext.

Anti-pattern

Publier un paquet en double format CJS+ESM « par sécurité » alors que la cible Node supporte require(esm) : deux copies, deux jeux de .d.ts, singletons dupliqués.
#core#modules#interview#node22

En entretien

  1. Quelle différence entre CommonJS et ESM ?

    CommonJS (require/module.exports) est synchrone et historique dans Node.js, tandis qu'ESM (import/export) est le standard du langage, statique et propice au tree shaking. Depuis Node 22.12.0/20.19.0, stable en 25.4.0, require() peut charger un module ESM synchrone, sauf s'il contient un top-level await.

  2. Aujourd'hui, pourquoi publier un paquet en double format CJS+ESM est souvent une mauvaise idée ?

    Parce que les consommateurs CommonJS peuvent désormais faire require(esm), publier en ESM seul devient viable. Le double format entraîne deux copies du code, deux jeux de .d.ts et des singletons dupliqués.

Dans l'actu

  • 16/09 Node.js 26.9.0 (Current) ajoute node:bench, un framework de benchmark intégré derrière le flag --experimental-bench, active le module FFI par défaut, et branche un système de fichiers virtuel (VFS) sur les loaders CommonJS et ESM avec un ZipProvider pour lire des archives ZIP. Semaine 38 · 14 – 20 septembre 2026 →
  • 16/09 Node.js 26.9.0 (Current) ajoute `node:bench` (derrière `--experimental-bench`), active le module FFI par défaut, branche un système de fichiers virtuel sur les loaders CommonJS et ESM et ajoute des Web Workers natifs. Bilan septembre 2026 →
  • 27/08 NestJS 12.0.0 est publié : tous les packages core passent en ESM (compatibilité CommonJS conservée via require(esm)), la validation accepte les bibliothèques Standard Schema comme Zod et Valibot, un SDK d'observabilité @nestjs/observe auto-instrumente HTTP/GraphQL/gRPC, et la CLI gagne « nest upgrade ». Bilan août 2026 →

Fiche mise à jour le 2026-09-27

← → au clavier pour passer d'une fiche à l'autre