Avant que le langage n'ait ses propres modules, Node avait besoin d'un moyen de découper un programme en fichiers. Il a adopté CommonJS, et des millions de paquets ont été publiés sous cette forme.
Le résultat se voit encore aujourd'hui : la plupart des projets serveur croisent les deux systèmes, et savoir lequel s'applique évite une bonne moitié des messages d'erreur au démarrage.
Définition
CommonJS est un système de modules dans lequel un fichier publie ses valeurs en les posant sur l'objet module.exports, et en récupère d'autres en appelant la fonction require(). Contrairement à un Module ES, tout se joue à l'exécution : require() est un appel de fonction comme un autre.
// panier.js
const TVA = 0.2;
function totalTTC(ht) {
return ht * (1 + TVA);
}
module.exports = { TVA, totalTTC };
// facture.js
const { totalTTC } = require('./panier.js');
console.log(totalTTC(100)); // 120L'extension peut être omise et le chemin sans point désigne un paquet installé, exactement comme avec import.
module.exports et exports, la confusion classique
Au démarrage, exports est un simple raccourci vers module.exports. Poser une clé sur l'un revient donc à la poser sur l'autre, et tout va bien.
Le piège apparaît dès qu'on remplace l'objet entier. module.exports = { b: 2 } coupe le lien : ce qui avait été ajouté à exports auparavant disparaît, et le fichier qui fait le require reçoit uniquement { b: 2 }. La règle est simple : choisissez une des deux écritures et tenez-la sur tout le fichier.
require() met chaque module en cache. Le deuxième appel sur le même chemin ne réexécute pas le fichier, il renvoie l'objet déjà construit. C'est ce qui permet de partager un état entre plusieurs fichiers, volontairement ou non.
Ce que CommonJS apporte, et ce qu'il coûte
| Point | Conséquence |
|---|---|
| Chargement synchrone | Le fichier est lu au moment de l'appel, ce qui bloque le reste |
| Chemin calculé accepté | require(nomVariable) fonctionne, mais rend l'analyse impossible |
| Analyse impossible | Un outil ne peut pas savoir ce qui est utilisé, donc pas de Tree shaking (élagage) |
| Variables fournies | __dirname et __filename existent, contrairement aux modules ES |
Questions fréquentes
Comment savoir si un fichier est traité en CommonJS ?
Par défaut, un .js est du CommonJS, sauf si le fichier package.json le plus proche déclare "type": "module". L'extension tranche dans tous les cas : .cjs impose CommonJS, .mjs impose les modules ES. En cas de doute, l'extension explicite règle la question définitivement.
Que signifie l'erreur « require is not defined » ?
Elle indique que le fichier est exécuté comme un module ES, où require n'existe pas. Deux issues : convertir le fichier en import, ce qui est préférable sur un projet neuf, ou le renommer en .cjs si le code environnant n'est pas prêt à changer.
CommonJS est-il dépassé ?
Il n'est pas abandonné, et une part énorme des paquets publiés reste écrite ainsi. Pour du code neuf, les modules ES sont le choix par défaut : ce sont ceux du langage, ils fonctionnent aussi dans le navigateur, et ils ouvrent la porte aux optimisations de Bundler (empaqueteur).