Utiliser les ES Modules dans Node.js en natif

La spécification ESM (EcmaScript Modules) introduite avec ES6 ou ES2015 décrit comment importer et exporter des modules en JavaScript. À présent il est possible d'utiliser ESM de façon quasi-native et sans l'aide de Babel.

De CommonJS à ES Modules

Avec le support ESM natif qui arrive dans les navigateurs, le fait de l'avoir aussi dans Node.js se fait sentir. Depuis sa création, Node.js utilise un système de module appelé CommonJS. Voici un comparatif des deux systèmes :

CJS:

const a = require("./a")
module.exports = { a, b: 2 }

ESM:

import a from "./a"
export default { a, b: 2 }

Cela peut paraître similaire au premier abord, mais passer de l'un à l'autre n'est pas simple et il s'avère que les deux syntaxes sont incompatibles. Pour plus de détails, je vous invite à lire l'excellent article de Nicolás Bevacqua sur le sujet.

Les contributeurs de Node.js ont donc pris le parti d'utiliser une nouvelle extension pour les fichiers utilisant la syntaxe ESM : .mjs. Vous l'aurez deviné, on a ajouté un "m" pour "modules" 😏.

Les modules devraient arriver de façon native dans la version 10 de Node.js, aux alentours d'avril 2018, mais d'ici là que faire ?

Quand on expose une librairie, plusieurs choix s'offrent à nous :

  • Ignorer l'existence des modules natifs et utiliser CommonJS 🤠
  • Utiliser les modules natifs et transpiler avec Babel 🤔
  • Exposer les deux systèmes 😵

Dans un projet, les choix sont plus restreints, soit on fait du CommonJS, soit on utilise Babel.

Il s'avère que Node.js a un support complet de ES7 et même ES8. Personnellement je trouve ça un peu dommage de devoir transpiler son code uniquement pour utiliser export et import vous ne trouvez pas ?

@std/esm

C'est là que @std/esm arrive à la rescousse ! Ce package créé par John Dalton (le créateur de lodash) permet d'utiliser ESM dans Node.js sans transpiler. Il fonctionne sans problème avec l'écosystème déjà en place comme Babel et Webpack.

Utiliser @std/esm dans son projet

Vous devez tout d'abord installer la librairie avec votre gestionnaire de package préféré : yarn add @std/esm ou npm install @std/esm.

Une fois installée, vous devez appeler require('@std/esm') avant d'importer des modules ESM, des fichiers .mjs donc pour ceux qui ont suivi.

La première étape consiste à créer un fichier main.mjs :

// main.mjs
import multiply from './multiply'

console.log(multiply(5, 2))

Notre fichier fait une multiplication, nous allons donc créer un fichier multiply.mjs qui exportera la fonction multiply().

// multiply.mjs

export default function multiply(a, b) {
  return a * b
}

Je sais cet exemple est un peu bateau, mais on est là pour comprendre comment utiliser @std/esm, pas pour recoder git 😒.

A partir de là, deux choix s'offrent à vous :

Utiliser la fonction -r de node pour charger un module avant de lancer votre programme :

$ node -r @std/esm main.mjs

Ou bien créer un fichier main.js qui servira de passe-plat pour lancer le vrai main.mjs :

// main.js
require = require('@std/esm')(module)
require('./main.mjs')

Ça y est, vous pouvez enfin vous débarrasser de Babel et utiliser l'ensemble des fonctionnalités de ES6 (2 ans après, il était temps 🙄) !

@std/esm en détail

La librairie a un support complet :

D'autres fonctionnalités sont "cachées", libre à vous de les activer en ajoutant une entrée "@std/esm" dans votre package.json. Les fonctionnalités activables à la demande sont les suivantes :

  • "esm" : vous permet de choisir quels fichiers seront chargés en mode ESM
    • "mjs" les fichiers avec une extension .mjs
    • "all" tous les fichiers
    • "js" les fichiers comportant import / export ou "use module"
  • "cjs" : vous permet d'activer le support de CJS en plus de celui de ESM, comme par exemple pouvoir appeler require dans un .mjs
  • "await" : permet d'utiliser await sans être dans une fonction
  • "gz" : permet d'activer le chargement des .js.gz et .mjs.gz via Webpack

Si vous souhaitez avoir plus de détails sur l'ensemble de ces fonctionnalités, je vous suggère d'aller directement sur le GitHub du package.

Encore merci à John Dalton pour la création de ce package et pour son article extrêmement détaillé sur le sujet.

Découvrez nos formations JavaScript