Comment utiliser les modules ECMAScript avec Node.js – CloudSavvy IT
Agence web » Actualités du digital » Comment utiliser les modules ECMAScript avec Node.js – CloudSavvy IT

Comment utiliser les modules ECMAScript avec Node.js – CloudSavvy IT

Un moyen standardisé de conditionner le code sous forme de modules réutilisables manquait à ECMAScript pendant la majeure partie de son histoire. En l’absence de solution intégrée, l’approche CommonJS (CJS) est devenue le standard de facto pour le développement de Node.js. Cela utilise require et module.exports consommer et fournir des morceaux de code :

// Import a module
const fs = require("fs");
 
// Provide an export
module.exports = () => "Hello World";

ES2015, également connu sous le nom d’ES6, a finalement introduit son propre système de modules intégrés. Les modules ECMAScript ou ES (ESM) reposent sur import et export syntaxe:

// Import a default export
import fs from "fs";
 
// Provide a default export
export default () => "Hello World";
 
// Import a named export
import {helloWorld} from "./hello-world.js";
 
// Provide a named export
export const helloWorld = () => "Hello World";

Node.js offre une prise en charge par défaut pour ESM depuis la v16. Dans les versions antérieures, vous deviez utiliser le --experimental-modules drapeau pour activer la capacité. Alors que les modules ES sont désormais marqués comme stables et prêts pour une utilisation générale, la présence de deux mécanismes de chargement de module différents signifie qu’il est difficile de consommer les deux types de code dans un seul projet.

Dans cet article, nous verrons comment utiliser les modules ES avec Node et ce que vous pouvez faire pour maximiser l’interopérabilité avec les packages CommonJS.

Les bases

Les choses sont relativement simples si vous démarrez un nouveau projet et que vous souhaitez compter sur ESM. Comme Node.js offre désormais une prise en charge complète, vous pouvez diviser votre code en fichiers séparés et utiliser import et export instructions pour accéder à vos modules.

Malheureusement, vous devez faire des choix conscients dès le début. Par défaut, Node ne prend pas en charge import et export à l’intérieur des fichiers qui se terminent par le .js extension. Vous pouvez soit suffixer vos fichiers comme .mjsoù ESM est toujours disponible, ou modifiez votre package.json fichier à inclure "type": "module".

{
    "name": "example-package",
    "type": "module",
    "dependencies": {
        "..."
    }
}

Le choix de cette dernière voie est généralement plus pratique pour les projets qui utiliseront exclusivement ESM. Le nœud utilise le type pour déterminer le système de module par défaut pour votre projet. Ce système de modules est toujours utilisé pour gérer .js des dossiers. Lorsqu’il n’est pas défini manuellement, CJS est le système de module par défaut pour maximiser la compatibilité avec l’écosystème existant de code Node. Fichiers avec soit .cjs ou .mjs les extensions seront toujours traitées comme source au format CJS et ESM respectivement.

Importation d’un module CommonJS à partir d’ESM

Vous pouvez importer des modules CJS dans des fichiers ESM à l’aide d’un import déclaration:

// cjs-module.cjs
module.exports.helloWorld = () => console.log("Hello World");
 
// esm-module.mjs
import component from "./cjs-module.cjs";
component.helloWorld();

component sera résolu à la valeur du module CJS module.exports. L’exemple ci-dessus montre comment vous pouvez accéder aux exportations nommées en tant que propriétés d’objet sur le nom de votre importation. Vous pouvez également accéder à des exportations spécifiques à l’aide de la syntaxe d’importation nommée ESM :

import {helloWorld} from "./cjs-module.cjs";
helloWorld();

Cela fonctionne au moyen d’un système d’analyse statique qui analyse les fichiers CJS pour déterminer les exportations qu’ils fournissent. Cette approche est nécessaire car CJS ne comprend pas le concept d’« exportation désignée ». Tous les modules CJS ont une exportation – les exportations « nommées » sont en réalité un objet avec plusieurs paires propriété-valeur.

En raison de la nature du processus d’analyse statique, il est possible que certains modèles de syntaxe rares ne soient pas détectés correctement. Si cela se produit, vous devrez plutôt accéder aux propriétés dont vous avez besoin via l’objet d’exportation par défaut.

Importation d’un module ESM à partir de CJS

Les choses se compliquent lorsque vous souhaitez utiliser un nouveau module ESM dans le code CJS existant. Vous ne pouvez pas écrire le import déclaration dans les fichiers CJS. Cependant la dynamique import() la syntaxe fonctionne et peut être associée à await pour accéder aux modules relativement facilement :

// esm-module.mjs
const helloWorld = () => console.log("Hello World");
export {helloWorld};
 
// esm-module-2.mjs
export default = () => console.log("Hello World");
 
// cjs-module.cjs
const loadHelloWorld = async () => {
    const {helloWorld} = await import("./esm-module.mjs");
    return helloWorld;
};
const helloWorld = await loadHelloWorld();
helloWorld();
 
const loadHelloWorld2 = async() => {
    const helloWorld2 = await import("./esm-module-2.mjs");
    return helloWorld2;
};
const helloWorld2 = await loadHelloWorld2();
helloWorld2();

Cette structure peut être utilisée pour accéder de manière asynchrone aux exportations par défaut et nommées de vos modules ESM.

Récupération du chemin du module actuel avec les modules ES

Les modules ES n’ont pas accès à toutes les variables globales Node.js familières disponibles dans les contextes CJS. outre require() et module.exportsvous ne pourrez pas accéder au __dirname ou __filename constantes non plus. Ceux-ci sont couramment utilisés par les modules CJS qui ont besoin de connaître le chemin d’accès à leur propre fichier.

Les fichiers ESM peuvent lire import.meta.url pour obtenir ces informations :

console.log(import.meta.url);
// file:///home/demo/module.mjs

L’URL retournée donne le chemin absolu vers le fichier courant.

Pourquoi toutes les incompatibilités ?

Les différences entre CJS et ESM sont beaucoup plus profondes que de simples changements syntaxiques. CJS est un système synchrone ; lorsque vous require() un module, Node le charge directement depuis le disque et exécute son contenu. L’ESM est asynchrone et divise les importations de scripts en plusieurs phases distinctes. Les importations sont analysées, chargées de manière asynchrone à partir de leur emplacement de stockage, puis exécutées une fois que toutes leurs propres importations ont été récupérées de la même manière.

ESM est conçu comme une solution moderne de chargement de modules avec de nombreuses applications. C’est pourquoi les modules ESM conviennent à une utilisation dans les navigateurs Web : ils sont asynchrones par conception, donc les réseaux lents ne sont pas un problème.

La nature asynchrone d’ESM est également responsable des limitations de son utilisation dans le code CJS. Les fichiers CJS ne prennent pas en charge le niveau supérieur await donc tu ne peux pas utiliser import à lui tout seul :

// this...
import component from "component.mjs";
 
// ...can be seen as equivalent to this...
const component = await import("component.mjs");
 
// ...but top-level "await" isn't available in CJS

Il faut donc utiliser la dynamique import() structure à l’intérieur d’un async une fonction.

Devriez-vous passer aux modules ES ?

La réponse simple est oui. Les modules ES sont le moyen standardisé d’importer et d’exporter du code JavaScript. CJS a donné à Node un système de modules lorsque le langage n’avait pas le sien. Maintenant qu’un est disponible, il est préférable pour la santé à long terme de la communauté d’adopter l’approche décrite par la norme ECMAScript.

ESM est également le système le plus puissant. Parce qu’il est asynchrone, vous obtenez des importations dynamiques, des importations à distance à partir d’URL et des performances améliorées dans certaines situations. Vous pouvez également réutiliser vos modules avec d’autres runtimes JavaScript, tels que du code livré directement aux navigateurs Web via <script type="module"> Balises HTML.

Néanmoins, la migration reste difficile pour de nombreux projets Node.js existants. CJS ne va pas disparaître de si tôt. En attendant, vous pouvez faciliter la transition en proposant à la fois des exportations CJS et ESM pour vos propres bibliothèques. Pour ce faire, le mieux est d’écrire un wrapper ESM fin autour de toutes les exportations CJS existantes :

import demoComponent from "../cjs-component-demo.js";
import exampleComponent from "../cjs-component-example.js";
export {demoComponent, exampleComponent};

Placez ce fichier dans un nouveau esm sous-répertoire de votre projet. Ajouter un package.json à ses côtés contenant le {"type": "module"} champ.

Ensuite, mettez à jour le package.json à la racine de votre projet pour inclure le contenu suivant :

{
    "exports": {
        "require": "./cjs-index.js",
        "import": "./esm/esm-index.js"
    }
}

Cela indique à Node de fournir votre point d’entrée CJS lorsque les utilisateurs de votre package require() une exportation. Lorsque import est utilisé, votre script wrapper ESM vous sera proposé à la place. Attention, cette fonctionnalité d’importation de cartes entraîne également un autre effet secondaire : les utilisateurs seront limités aux seules exportations fournies par votre fichier de point d’entrée. Chargement de fichiers spécifiques via des chemins intégrés au package (import demo from "example-package/path/to/file.js) est désactivé lorsqu’une carte d’importation est présente.

Sommaire

Le paysage du module Node.js peut être pénible, en particulier lorsque vous avez besoin d’une compatibilité avec CJS et ESM. Bien que les choses se soient améliorées avec la stabilisation de la prise en charge ESM, vous devez toujours décider à l’avance quel système de module doit être le « par défaut » pour votre projet. Le chargement de code à partir de «l’autre» système est possible mais souvent inconfortable, en particulier lorsque CJS dépend d’ESM.

Les choses s’amélioreront progressivement au fur et à mesure que de plus en plus de projets majeurs passeront à l’adoption de l’ESM comme approche préférée. Pourtant, avec autant de code CJS existant, la migration en gros est irréaliste et il est probable que les deux mécanismes seront utilisés côte à côte – avec tous les compromis que cela implique – pendant encore plusieurs années à venir.

Si vous démarrez une nouvelle bibliothèque, assurez-vous de distribuer du code compatible ESM et essayez de l’utiliser comme système par défaut dans la mesure du possible. Cela devrait aider l’écosystème Node à converger vers ESM, en le réalignant sur les normes ES.

★★★★★