Modules : Paquets
[Historique]
Version | Modifications |
---|---|
v14.13.0, v12.20.0 | Ajout de la prise en charge des modèles "exports" . |
v14.6.0, v12.19.0 | Ajout du champ "imports" du paquet. |
v13.7.0, v12.17.0 | Suppression de l’indicateur des exportations conditionnelles. |
v13.7.0, v12.16.0 | Suppression de l’option --experimental-conditional-exports . Dans la version 12.16.0, les exportations conditionnelles sont toujours masquées derrière --experimental-modules . |
v13.6.0, v12.16.0 | Suppression de l’indicateur d’autoréférencement d’un paquet à l’aide de son nom. |
v12.7.0 | Introduction du champ "exports" de package.json comme alternative plus puissante au champ classique "main" . |
v12.0.0 | Ajout de la prise en charge des modules ES à l’aide de l’extension de fichier .js via le champ "type" de package.json . |
Introduction
Un paquet est une arborescence de dossiers décrite par un fichier package.json
. Le paquet est constitué du dossier contenant le fichier package.json
et de tous les sous-dossiers jusqu’au dossier suivant contenant un autre fichier package.json
ou un dossier nommé node_modules
.
Cette page fournit des conseils aux auteurs de paquets qui écrivent des fichiers package.json
, ainsi qu’une référence aux champs package.json
définis par Node.js.
Détermination du système de modules
Introduction
Node.js traitera ce qui suit comme des modules ES lorsqu’il est transmis à node
comme entrée initiale ou lorsqu’il est référencé par des instructions import
ou des expressions import()
:
- Les fichiers avec une extension
.mjs
. - Les fichiers avec une extension
.js
lorsque le fichierpackage.json
parent le plus proche contient un champ"type"
de niveau supérieur avec la valeur"module"
. - Les chaînes transmises en argument à
--eval
ou transmises ànode
viaSTDIN
, avec l’indicateur--input-type=module
. - Le code contenant une syntaxe uniquement analysée avec succès en tant que modules ES, tels que les instructions
import
ouexport
ouimport.meta
, sans marqueur explicite de la manière dont il doit être interprété. Les marqueurs explicites sont les extensions.mjs
ou.cjs
, les champspackage.json
"type"
avec les valeurs"module"
ou"commonjs"
, ou l’indicateur--input-type
. Les expressionsimport()
dynamiques sont prises en charge dans les modules CommonJS ou ES et n’obligeraient pas un fichier à être traité comme un module ES. Voir Détection de la syntaxe.
Node.js traitera ce qui suit comme CommonJS lorsqu’il est transmis à node
comme entrée initiale ou lorsqu’il est référencé par des instructions import
ou des expressions import()
:
- Les fichiers avec une extension
.cjs
. - Les fichiers avec une extension
.js
lorsque le fichierpackage.json
parent le plus proche contient un champ de niveau supérieur"type"
avec la valeur"commonjs"
. - Les chaînes transmises en argument à
--eval
ou--print
, ou transmises ànode
viaSTDIN
, avec l’indicateur--input-type=commonjs
. - Les fichiers avec une extension
.js
sans fichierpackage.json
parent ou lorsque le fichierpackage.json
parent le plus proche n’a pas de champtype
, et où le code peut être évalué avec succès en tant que CommonJS. En d’autres termes, Node.js essaie d’abord d’exécuter ces fichiers « ambigus » en tant que CommonJS, puis essaiera de les évaluer en tant que modules ES si l’évaluation en tant que CommonJS échoue parce que l’analyseur a trouvé la syntaxe du module ES.
L’écriture de la syntaxe du module ES dans des fichiers « ambigus » entraîne un coût de performance. Il est donc conseillé aux auteurs d’être explicites dans la mesure du possible. En particulier, les auteurs de paquets doivent toujours inclure le champ "type"
dans leurs fichiers package.json
, même dans les paquets où toutes les sources sont CommonJS. Le fait d’être explicite quant au type
du paquet permettra de pérenniser le paquet au cas où le type par défaut de Node.js changerait un jour, et facilitera également la tâche des outils de création et des chargeurs pour déterminer comment les fichiers du paquet doivent être interprétés.
Détection de la syntaxe
[Historique]
Version | Changements |
---|---|
v22.7.0 | La détection de la syntaxe est activée par défaut. |
v21.1.0, v20.10.0 | Ajouté dans : v21.1.0, v20.10.0 |
[Stable: 1 - Expérimental]
Stable: 1 Stabilité: 1.2 - Candidat à la publication
Node.js inspectera le code source d'une entrée ambigüe pour déterminer si elle contient une syntaxe de module ES ; si une telle syntaxe est détectée, l'entrée sera traitée comme un module ES.
Une entrée ambigüe est définie comme :
- Les fichiers avec une extension
.js
ou sans extension ; et soit aucun fichierpackage.json
de contrôle, soit un fichier qui n'a pas de champtype
. - Entrée de chaîne (
--eval
ouSTDIN
) lorsque--input-type
n'est pas spécifié.
La syntaxe du module ES est définie comme une syntaxe qui lèverait une erreur lorsqu'elle est évaluée en tant que CommonJS. Cela inclut les éléments suivants :
- Déclarations
import
(mais pas les expressionsimport()
, qui sont valides dans CommonJS). - Déclarations
export
. - Références
import.meta
. await
au niveau supérieur d'un module.- Redéclarations lexicales des variables wrapper CommonJS (
require
,module
,exports
,__dirname
,__filename
).
Chargeurs de modules
Node.js dispose de deux systèmes pour résoudre un spécificateur et charger des modules.
Il y a le chargeur de modules CommonJS :
- Il est entièrement synchrone.
- Il est responsable de la gestion des appels
require()
. - Il peut être patché par monkey patching.
- Il prend en charge les dossiers en tant que modules.
- Lors de la résolution d'un spécificateur, si aucune correspondance exacte n'est trouvée, il essaiera d'ajouter des extensions (
.js
,.json
, et enfin.node
) puis tentera de résoudre les dossiers en tant que modules. - Il traite
.json
comme des fichiers texte JSON. - Les fichiers
.node
sont interprétés comme des modules complémentaires compilés chargés avecprocess.dlopen()
. - Il traite tous les fichiers qui n'ont pas d'extensions
.json
ou.node
comme des fichiers texte JavaScript. - Il ne peut être utilisé pour charger des modules ECMAScript à partir de modules CommonJS que si le graphe de modules est synchrone (qui ne contient pas de
await
de niveau supérieur). Lorsqu'il est utilisé pour charger un fichier texte JavaScript qui n'est pas un module ECMAScript, le fichier sera chargé comme un module CommonJS.
Il y a le chargeur de module ECMAScript :
- Il est asynchrone, sauf s'il est utilisé pour charger des modules pour
require()
. - Il est responsable de la gestion des déclarations
import
et des expressionsimport()
. - Il n'est pas patchable par monkey patching, il peut être personnalisé à l'aide des hooks de chargeur.
- Il ne prend pas en charge les dossiers en tant que modules, les index de répertoires (par exemple,
'./startup/index.js'
) doivent être entièrement spécifiés. - Il n'effectue aucune recherche d'extension. Une extension de fichier doit être fournie lorsque le spécificateur est une URL de fichier relative ou absolue.
- Il peut charger des modules JSON, mais un attribut de type import est requis.
- Il n'accepte que les extensions
.js
,.mjs
et.cjs
pour les fichiers texte JavaScript. - Il peut être utilisé pour charger des modules JavaScript CommonJS. Ces modules sont transmis via
cjs-module-lexer
pour essayer d'identifier les exportations nommées, qui sont disponibles si elles peuvent être déterminées par une analyse statique. Les modules CommonJS importés ont leurs URL converties en chemins absolus, puis sont chargés via le chargeur de modules CommonJS.
package.json
et extensions de fichiers
Au sein d'un paquet, le champ "type"
du fichier package.json
définit la façon dont Node.js doit interpréter les fichiers .js
. Si un fichier package.json
n'a pas de champ "type"
, les fichiers .js
sont traités comme CommonJS.
Une valeur "module"
pour le champ "type"
d'un package.json
indique à Node.js d'interpréter les fichiers .js
de ce paquet comme utilisant la syntaxe des modules ES.
Le champ "type"
s'applique non seulement aux points d'entrée initiaux (node my-app.js
) mais aussi aux fichiers référencés par les instructions import
et les expressions import()
.
// my-app.js, traité comme un module ES car il existe un fichier package.json
// dans le même dossier avec "type": "module".
import './startup/init.js'
// Chargé comme module ES car ./startup ne contient pas de fichier package.json,
// et hérite donc de la valeur "type" du niveau supérieur.
import 'commonjs-package'
// Chargé comme CommonJS car ./node_modules/commonjs-package/package.json
// n'a pas de champ "type" ou contient "type": "commonjs".
import './node_modules/commonjs-package/index.js'
// Chargé comme CommonJS car ./node_modules/commonjs-package/package.json
// n'a pas de champ "type" ou contient "type": "commonjs".
Les fichiers se terminant par .mjs
sont toujours chargés comme des modules ES quel que soit le fichier package.json
parent le plus proche.
Les fichiers se terminant par .cjs
sont toujours chargés comme CommonJS quel que soit le fichier package.json
parent le plus proche.
import './legacy-file.cjs'
// Chargé comme CommonJS car .cjs est toujours chargé comme CommonJS.
import 'commonjs-package/src/index.mjs'
// Chargé comme module ES car .mjs est toujours chargé comme module ES.
Les extensions .mjs
et .cjs
peuvent être utilisées pour mélanger les types au sein d'un même paquet :
- Au sein d'un paquet
"type": "module"
, Node.js peut être incité à interpréter un fichier particulier comme CommonJS en le nommant avec une extension.cjs
(car les fichiers.js
et.mjs
sont traités comme des modules ES au sein d'un paquet"module"
). - Au sein d'un paquet
"type": "commonjs"
, Node.js peut être incité à interpréter un fichier particulier comme un module ES en le nommant avec une extension.mjs
(car les fichiers.js
et.cjs
sont traités comme CommonJS au sein d'un paquet"commonjs"
).
Indicateur --input-type
Ajouté dans : v12.0.0
Les chaînes passées en argument à --eval
(ou -e
), ou envoyées à node
via STDIN
, sont traitées comme des modules ES lorsque l’indicateur --input-type=module
est défini.
node --input-type=module --eval "import { sep } from 'node:path'; console.log(sep);"
echo "import { sep } from 'node:path'; console.log(sep);" | node --input-type=module
Pour être exhaustif, il existe également --input-type=commonjs
, pour exécuter explicitement l’entrée de chaîne comme CommonJS. C’est le comportement par défaut si --input-type
n’est pas spécifié.
Détermination du gestionnaire de packages
[Stable : 1 - Expérimental]
Stable : 1 Stabilité : 1 - Expérimental
Bien que tous les projets Node.js doivent pouvoir être installés par tous les gestionnaires de packages une fois publiés, leurs équipes de développement sont souvent tenues d’utiliser un gestionnaire de packages spécifique. Pour faciliter ce processus, Node.js est livré avec un outil appelé Corepack qui vise à rendre tous les gestionnaires de packages disponibles de manière transparente dans votre environnement, à condition que Node.js soit installé.
Par défaut, Corepack n’imposera aucun gestionnaire de packages spécifique et utilisera les versions génériques « Last Known Good » associées à chaque version de Node.js, mais vous pouvez améliorer cette expérience en définissant le champ "packageManager"
dans le fichier package.json
de votre projet.
Points d’entrée de package
Dans le fichier package.json
d’un package, deux champs peuvent définir les points d’entrée d’un package : "main"
et "exports"
. Les deux champs s’appliquent aux points d’entrée de modules ES et de modules CommonJS.
Le champ "main"
est pris en charge dans toutes les versions de Node.js, mais ses capacités sont limitées : il ne définit que le point d’entrée principal du package.
Le champ "exports"
offre une alternative moderne à "main"
, permettant de définir plusieurs points d’entrée, la prise en charge de la résolution conditionnelle des entrées entre les environnements et empêchant tout autre point d’entrée autre que ceux définis dans "exports"
. Cette encapsulation permet aux auteurs de modules de définir clairement l’interface publique de leur package.
Pour les nouveaux packages ciblant les versions actuellement prises en charge de Node.js, le champ "exports"
est recommandé. Pour les packages prenant en charge Node.js 10 et les versions antérieures, le champ "main"
est requis. Si les champs "exports"
et "main"
sont tous deux définis, le champ "exports"
est prioritaire sur le champ "main"
dans les versions prises en charge de Node.js.
Des exports conditionnels peuvent être utilisés dans "exports"
pour définir différents points d’entrée de package par environnement, notamment si le package est référencé via require
ou via import
. Pour plus d’informations sur la prise en charge des modules CommonJS et ES dans un seul package, veuillez consulter la section des packages doubles CommonJS/modules ES.
Les packages existants introduisant le champ "exports"
empêcheront les consommateurs du package d’utiliser tous les points d’entrée qui ne sont pas définis, y compris le package.json
(par ex. require('your-package/package.json')
). Il s’agira probablement d’une modification importante.
Pour que l’introduction de "exports"
ne soit pas une modification importante, assurez-vous que tous les points d’entrée précédemment pris en charge soient exportés. Il est préférable de spécifier explicitement les points d’entrée afin que l’API publique du package soit bien définie. Par exemple, un projet qui exportait auparavant main
, lib
, feature
et le fichier package.json
pourrait utiliser le package.exports
suivant :
{
"name": "my-package",
"exports": {
".": "./lib/index.js",
"./lib": "./lib/index.js",
"./lib/index": "./lib/index.js",
"./lib/index.js": "./lib/index.js",
"./feature": "./feature/index.js",
"./feature/index": "./feature/index.js",
"./feature/index.js": "./feature/index.js",
"./package.json": "./package.json"
}
}
Alternativement, un projet peut choisir d’exporter des dossiers entiers avec et sans sous-chemins d’extension en utilisant des modèles d’exportation :
{
"name": "my-package",
"exports": {
".": "./lib/index.js",
"./lib": "./lib/index.js",
"./lib/*": "./lib/*.js",
"./lib/*.js": "./lib/*.js",
"./feature": "./feature/index.js",
"./feature/*": "./feature/*.js",
"./feature/*.js": "./feature/*.js",
"./package.json": "./package.json"
}
}
Ce qui précède assure la rétrocompatibilité pour toutes les versions mineures du package, une modification majeure future pour le package peut ensuite restreindre correctement les exports aux exports de fonctionnalités spécifiques exposées :
{
"name": "my-package",
"exports": {
".": "./lib/index.js",
"./feature/*.js": "./feature/*.js",
"./feature/internal/*": null
}
}
Point d'entrée principal exporté
Lors de l'écriture d'un nouveau package, il est recommandé d'utiliser le champ "exports"
:
{
"exports": "./index.js"
}
Lorsque le champ "exports"
est défini, tous les sous-chemins du package sont encapsulés et ne sont plus disponibles pour les importateurs. Par exemple, require('pkg/subpath.js')
génère une erreur ERR_PACKAGE_PATH_NOT_EXPORTED
.
Cette encapsulation des exports fournit des garanties plus fiables sur les interfaces de package pour les outils et lors de la gestion des mises à niveau semver pour un package. Il ne s'agit pas d'une encapsulation forte, car une exigence directe de tout sous-chemin absolu du package tel que require('/path/to/node_modules/pkg/subpath.js')
chargera toujours subpath.js
.
Toutes les versions actuellement prises en charge de Node.js et les outils de construction modernes prennent en charge le champ "exports"
. Pour les projets utilisant une ancienne version de Node.js ou un outil de construction associé, la compatibilité peut être obtenue en incluant le champ "main"
à côté de "exports"
pointant vers le même module :
{
"main": "./index.js",
"exports": "./index.js"
}
Exports de sous-chemins
Ajouté dans : v12.7.0
Lors de l'utilisation du champ "exports"
, des sous-chemins personnalisés peuvent être définis avec le point d'entrée principal en traitant le point d'entrée principal comme le sous-chemin "."
:
{
"exports": {
".": "./index.js",
"./submodule.js": "./src/submodule.js"
}
}
Désormais, seul le sous-chemin défini dans "exports"
peut être importé par un consommateur :
import submodule from 'es-module-package/submodule.js'
// Charge ./node_modules/es-module-package/src/submodule.js
Alors que d'autres sous-chemins généreront une erreur :
import submodule from 'es-module-package/private-module.js'
// Génère ERR_PACKAGE_PATH_NOT_EXPORTED
Extensions dans les sous-chemins
Les auteurs de packages doivent fournir des sous-chemins avec extension (import 'pkg/subpath.js'
) ou sans extension (import 'pkg/subpath'
) dans leurs exports. Cela garantit qu'il n'y a qu'un seul sous-chemin pour chaque module exporté, de sorte que tous les dépendants importent le même spécificateur cohérent, en gardant le contrat de package clair pour les consommateurs et en simplifiant les complétions de sous-chemin de package.
Traditionnellement, les packages avaient tendance à utiliser le style sans extension, ce qui présente les avantages de la lisibilité et du masquage du vrai chemin du fichier dans le package.
Les cartes d'importation fournissant désormais une norme pour la résolution des packages dans les navigateurs et autres environnements d'exécution JavaScript, l'utilisation du style sans extension peut entraîner des définitions de cartes d'importation volumineuses. Les extensions de fichiers explicites peuvent éviter ce problème en permettant à la carte d'importation d'utiliser un mappage de dossier de packages pour mapper plusieurs sous-chemins lorsque cela est possible au lieu d'une entrée de carte distincte par export de sous-chemin de package. Cela reflète également l'exigence d'utiliser le chemin du spécificateur complet dans les spécificateurs d'importation relatifs et absolus.
Exportations simplifiées
Ajouté en : v12.11.0
Si l’exportation "."
est la seule exportation, le champ "exports"
fournit une simplification dans ce cas, qui correspond à la valeur directe du champ "exports"
.
{
"exports": {
".": "./index.js"
}
}
peut être écrit :
{
"exports": "./index.js"
}
Importations de sous-chemins
Ajouté en : v14.6.0, v12.19.0
En plus du champ "exports"
, il existe un champ de package "imports"
pour créer des mappages privés qui ne s’appliquent qu’aux spécificateurs d’importation provenant de l’intérieur du package lui-même.
Les entrées du champ "imports"
doivent toujours commencer par #
pour garantir qu’elles sont différenciées des spécificateurs de packages externes.
Par exemple, le champ imports peut être utilisé pour bénéficier des avantages des exportations conditionnelles pour les modules internes :
// package.json
{
"imports": {
"#dep": {
"node": "dep-node-native",
"default": "./dep-polyfill.js"
}
},
"dependencies": {
"dep-node-native": "^1.0.0"
}
}
où import '#dep'
n’obtient pas la résolution du package externe dep-node-native
(y compris ses exportations à son tour), mais obtient le fichier local ./dep-polyfill.js
relatif au package dans d’autres environnements.
Contrairement au champ "exports"
, le champ "imports"
permet de mapper vers des packages externes.
Les règles de résolution pour le champ imports sont par ailleurs analogues au champ exports.
Modèles de sous-chemins
[Historique]
Version | Modifications |
---|---|
v16.10.0, v14.19.0 | Prise en charge des marqueurs de modèle dans le champ "imports". |
v16.9.0, v14.19.0 | Prise en charge des marqueurs de modèle. |
v14.13.0, v12.20.0 | Ajouté en : v14.13.0, v12.20.0 |
Pour les packages avec un petit nombre d’exportations ou d’importations, nous recommandons de répertorier explicitement chaque entrée de sous-chemin d’exportation. Mais pour les packages qui ont un grand nombre de sous-chemins, cela peut provoquer un gonflement de package.json
et des problèmes de maintenance.
Pour ces cas d’utilisation, des modèles d’exportation de sous-chemins peuvent être utilisés à la place :
// ./node_modules/es-module-package/package.json
{
"exports": {
"./features/*.js": "./src/features/*.js"
},
"imports": {
"#internal/*.js": "./src/internal/*.js"
}
}
*
Les mappages exposent des sous-chemins imbriqués car il s’agit uniquement d’une syntaxe de remplacement de chaîne.
Toutes les instances de *
du côté droit seront alors remplacées par cette valeur, y compris si elle contient des séparateurs /
.
import featureX from 'es-module-package/features/x.js'
// Charge ./node_modules/es-module-package/src/features/x.js
import featureY from 'es-module-package/features/y/y.js'
// Charge ./node_modules/es-module-package/src/features/y/y.js
import internalZ from '#internal/z.js'
// Charge ./node_modules/es-module-package/src/internal/z.js
Il s’agit d’une correspondance et d’un remplacement statiques directs sans traitement spécial pour les extensions de fichiers. L’inclusion de "*.js"
des deux côtés du mappage limite les exportations de package exposées aux seuls fichiers JS.
La propriété des exportations étant statiquement énumérable est maintenue avec des modèles d’exportations, car les exportations individuelles pour un package peuvent être déterminées en traitant le modèle cible du côté droit comme un glob **
par rapport à la liste des fichiers du package. Étant donné que les chemins node_modules
sont interdits dans les cibles d’exportations, cette expansion dépend uniquement des fichiers du package lui-même.
Pour exclure les sous-dossiers privés des modèles, des cibles null
peuvent être utilisées :
// ./node_modules/es-module-package/package.json
{
"exports": {
"./features/*.js": "./src/features/*.js",
"./features/private-internal/*": null
}
}
import featureInternal from 'es-module-package/features/private-internal/m.js'
// Lance : ERR_PACKAGE_PATH_NOT_EXPORTED
import featureX from 'es-module-package/features/x.js'
// Charge ./node_modules/es-module-package/src/features/x.js
Exportations conditionnelles
[Historique]
Version | Changements |
---|---|
v13.7.0, v12.16.0 | Suppression du flag des exportations conditionnelles. |
v13.2.0, v12.16.0 | Ajouté dans : v13.2.0, v12.16.0 |
Les exportations conditionnelles permettent de mapper vers différents chemins en fonction de certaines conditions. Elles sont prises en charge pour les importations CommonJS et les importations de modules ES.
Par exemple, un package qui souhaite fournir différentes exportations de modules ES pour require()
et import
peut être écrit comme suit :
// package.json
{
"exports": {
"import": "./index-module.js",
"require": "./index-require.cjs"
},
"type": "module"
}
Node.js implémente les conditions suivantes, classées de la plus spécifique à la moins spécifique, car les conditions doivent être définies de cette manière :
"node-addons"
- similaire à"node"
et correspond à tout environnement Node.js. Cette condition peut être utilisée pour fournir un point d'entrée qui utilise des addons C++ natifs par opposition à un point d'entrée qui est plus universel et ne repose pas sur des addons natifs. Cette condition peut être désactivée via le flag--no-addons
."node"
- correspond à tout environnement Node.js. Peut être un fichier CommonJS ou un module ES. Dans la plupart des cas, il n’est pas nécessaire d’appeler explicitement la plateforme Node.js."import"
- correspond lorsque le package est chargé viaimport
ouimport()
, ou via toute opération d’importation ou de résolution de niveau supérieur par le chargeur de modules ECMAScript. S’applique quel que soit le format de module du fichier cible. Toujours mutuellement exclusif avec"require"
."require"
- correspond lorsque le package est chargé viarequire()
. Le fichier référencé doit pouvoir être chargé avecrequire()
, bien que la condition corresponde quel que soit le format de module du fichier cible. Les formats attendus incluent CommonJS, JSON, les addons natifs et les modules ES. Toujours mutuellement exclusif avec"import"
."module-sync"
- correspond quel que soit le chargement du package viaimport
,import()
ourequire()
. Le format est censé être celui des modules ES qui ne contiennent pas d’await de niveau supérieur dans leur graphique de modules. Si c’est le cas,ERR_REQUIRE_ASYNC_MODULE
sera levée lorsque le module est requis avecrequire()
."default"
- la solution de repli générique qui correspond toujours. Peut être un fichier CommonJS ou un module ES. Cette condition doit toujours venir en dernier.
Dans l’objet "exports"
, l’ordre des clés est important. Lors de la correspondance des conditions, les entrées antérieures ont une priorité plus élevée et prennent le pas sur les entrées ultérieures. La règle générale est que les conditions doivent aller de la plus spécifique à la moins spécifique dans l’ordre des objets.
L’utilisation des conditions "import"
et "require"
peut entraîner certains dangers, qui sont expliqués plus en détail dans la section sur les packages à double module CommonJS/ES.
La condition "node-addons"
peut être utilisée pour fournir un point d’entrée qui utilise des addons C++ natifs. Cependant, cette condition peut être désactivée via le flag --no-addons
. Lors de l’utilisation de "node-addons"
, il est recommandé de traiter "default"
comme une amélioration qui fournit un point d’entrée plus universel, par exemple en utilisant WebAssembly au lieu d’un addon natif.
Les exportations conditionnelles peuvent également être étendues aux sous-chemins d’exportation, par exemple :
{
"exports": {
".": "./index.js",
"./feature.js": {
"node": "./feature-node.js",
"default": "./feature.js"
}
}
}
Définit un package où require('pkg/feature.js')
et import 'pkg/feature.js'
pourraient fournir différentes implémentations entre Node.js et d’autres environnements JS.
Lors de l’utilisation de branches d’environnement, incluez toujours une condition "default"
lorsque cela est possible. Fournir une condition "default"
garantit que tous les environnements JS inconnus sont en mesure d’utiliser cette implémentation universelle, ce qui permet d’éviter que ces environnements JS n’aient à prétendre être des environnements existants afin de prendre en charge les packages avec des exportations conditionnelles. Pour cette raison, l’utilisation de branches de condition "node"
et "default"
est généralement préférable à l’utilisation de branches de condition "node"
et "browser"
.
Conditions imbriquées
En plus des mappages directs, Node.js prend également en charge les objets de condition imbriqués.
Par exemple, pour définir un package qui n'a que des points d'entrée en mode double pour une utilisation dans Node.js mais pas dans le navigateur :
{
"exports": {
"node": {
"import": "./feature-node.mjs",
"require": "./feature-node.cjs"
},
"default": "./feature.mjs"
}
}
Les conditions continuent d'être comparées dans l'ordre comme avec les conditions plates. Si une condition imbriquée n'a pas de mappage, elle continue de vérifier les conditions restantes de la condition parent. De cette manière, les conditions imbriquées se comportent de manière analogue aux instructions if
imbriquées de JavaScript.
Résolution des conditions utilisateur
Ajouté dans : v14.9.0, v12.19.0
Lors de l'exécution de Node.js, des conditions utilisateur personnalisées peuvent être ajoutées avec l'indicateur --conditions
:
node --conditions=development index.js
ce qui résoudrait alors la condition "development"
dans les importations et exportations de packages, tout en résolvant les conditions existantes "node"
, "node-addons"
, "default"
, "import"
et "require"
de manière appropriée.
Un nombre quelconque de conditions personnalisées peut être défini avec des indicateurs répétés.
Les conditions typiques ne doivent contenir que des caractères alphanumériques, en utilisant ":", "-" ou "=" comme séparateurs si nécessaire. Tout autre élément peut entraîner des problèmes de compatibilité en dehors de Node.
Dans Node, les conditions ont très peu de restrictions, mais celles-ci incluent spécifiquement :
Définitions des conditions de la communauté
Les chaînes de conditions autres que les conditions "import"
, "require"
, "node"
, "module-sync"
, "node-addons"
et "default"
implémentées dans le cœur de Node.js sont ignorées par défaut.
D'autres plateformes peuvent implémenter d'autres conditions et des conditions utilisateur peuvent être activées dans Node.js via l'indicateur --conditions
/ -C
.
Étant donné que les conditions de package personnalisées nécessitent des définitions claires pour garantir une utilisation correcte, une liste de conditions de package courantes connues et leurs définitions strictes est fournie ci-dessous pour faciliter la coordination de l'écosystème.
"types"
: peut être utilisé par les systèmes de typage pour résoudre le fichier de typage de l'exportation donnée. Cette condition doit toujours être incluse en premier."browser"
: tout environnement de navigateur web."development"
: peut être utilisé pour définir un point d'entrée d'environnement réservé au développement, par exemple pour fournir un contexte de débogage supplémentaire tel que de meilleurs messages d'erreur lors de l'exécution en mode de développement. Doit toujours être mutuellement exclusif avec"production"
."production"
: peut être utilisé pour définir un point d'entrée d'environnement de production. Doit toujours être mutuellement exclusif avec"development"
.
Pour les autres runtimes, les définitions de clés de conditions spécifiques à la plateforme sont gérées par WinterCG dans la spécification de la proposition Clés de runtime.
De nouvelles définitions de conditions peuvent être ajoutées à cette liste en créant une demande d'extraction vers la documentation Node.js pour cette section. Les exigences pour répertorier une nouvelle définition de condition ici sont que :
- La définition doit être claire et non ambiguë pour tous les implémenteurs.
- Le cas d'utilisation justifiant la nécessité de la condition doit être clairement justifié.
- Il doit exister une utilisation d'implémentation existante suffisante.
- Le nom de la condition ne doit pas être en conflit avec une autre définition de condition ou une condition largement utilisée.
- La liste de la définition de condition doit apporter un avantage de coordination à l'écosystème qui ne serait pas possible autrement. Par exemple, ce ne serait pas nécessairement le cas pour les conditions spécifiques à l'entreprise ou à l'application.
- La condition doit être telle qu'un utilisateur Node.js s'attendrait à ce qu'elle figure dans la documentation principale de Node.js. La condition
"types"
en est un bon exemple : elle n'a pas vraiment sa place dans la proposition Clés de runtime, mais elle est tout à fait à sa place ici, dans la documentation Node.js.
Les définitions ci-dessus peuvent être déplacées vers un registre de conditions dédié en temps voulu.
Auto-référencement d'un paquet en utilisant son nom
[Historique]
Version | Changements |
---|---|
v13.6.0, v12.16.0 | Suppression du drapeau pour l'auto-référencement d'un paquet en utilisant son nom. |
v13.1.0, v12.16.0 | Ajouté dans : v13.1.0, v12.16.0 |
Au sein d'un paquet, les valeurs définies dans le champ "exports"
du package.json
du paquet peuvent être référencées via le nom du paquet. Par exemple, en supposant que le package.json
est :
// package.json
{
"name": "a-package",
"exports": {
".": "./index.mjs",
"./foo.js": "./foo.js"
}
}
Alors tout module dans ce paquet peut référencer une exportation dans le paquet lui-même :
// ./a-module.mjs
import { something } from 'a-package' // Importe "something" depuis ./index.mjs.
L'auto-référencement est disponible uniquement si package.json
a "exports"
, et permettra d'importer uniquement ce que "exports"
(dans le package.json
) autorise. Ainsi, le code ci-dessous, étant donné le paquet précédent, générera une erreur d'exécution :
// ./another-module.mjs
// Importe "another" depuis ./m.mjs. Échoue car
// le champ "exports" de "package.json"
// ne fournit pas d'export nommé "./m.mjs".
import { another } from 'a-package/m.mjs'
L'auto-référencement est également disponible lors de l'utilisation de require
, à la fois dans un module ES et dans un module CommonJS. Par exemple, ce code fonctionnera également :
// ./a-module.js
const { something } = require('a-package/foo.js') // Charge depuis ./foo.js.
Enfin, l'auto-référencement fonctionne également avec les paquets étendus. Par exemple, ce code fonctionnera également :
// package.json
{
"name": "@my/package",
"exports": "./index.js"
}
// ./index.js
module.exports = 42
// ./other.js
console.log(require('@my/package'))
$ node other.js
42
Paquets doubles CommonJS/module ES
Consultez le dépôt d'exemples de paquets pour plus de détails.
Définitions des champs package.json
de Node.js
Cette section décrit les champs utilisés par l'exécution de Node.js. D'autres outils (tels que npm) utilisent des champs supplémentaires qui sont ignorés par Node.js et non documentés ici.
Les champs suivants dans les fichiers package.json
sont utilisés dans Node.js :
"name"
- Pertinent lors de l'utilisation d'importations nommées dans un paquet. Également utilisé par les gestionnaires de paquets comme nom du paquet."main"
- Le module par défaut lors du chargement du paquet, si les exportations ne sont pas spécifiées, et dans les versions de Node.js antérieures à l'introduction des exportations."packageManager"
- Le gestionnaire de paquets recommandé lors de la contribution au paquet. Exploité par les shims Corepack."type"
- Le type de paquet déterminant s'il faut charger les fichiers.js
comme CommonJS ou comme modules ES."exports"
- Exportations de paquets et exportations conditionnelles. Lorsqu'il est présent, limite les sous-modules qui peuvent être chargés à partir du paquet."imports"
- Importations de paquets, à utiliser par les modules dans le paquet lui-même.
"name"
[Historique]
Version | Modifications |
---|---|
v13.6.0, v12.16.0 | Suppression de l'option --experimental-resolve-self . |
v13.1.0, v12.16.0 | Ajouté dans : v13.1.0, v12.16.0 |
- Type : <string>
{
"name": "nom-du-paquet"
}
Le champ "name"
définit le nom de votre paquet. La publication dans le registre npm nécessite un nom qui satisfait à certaines exigences.
Le champ "name"
peut être utilisé en complément du champ "exports"
pour s'auto-référencer un paquet en utilisant son nom.
"main"
Ajouté dans : v0.4.0
- Type : <string>
{
"main": "./index.js"
}
Le champ "main"
définit le point d'entrée d'un paquet lorsqu'il est importé par son nom via une recherche node_modules
. Sa valeur est un chemin.
Lorsqu'un paquet possède un champ "exports"
, celui-ci prévaudra sur le champ "main"
lors de l'importation du paquet par son nom.
Il définit également le script qui est utilisé lorsque le répertoire du paquet est chargé via require()
.
// Ceci renvoie à ./chemin/vers/repertoire/index.js.
require('./chemin/vers/repertoire')
"packageManager"
Ajouté dans : v16.9.0, v14.19.0
[Stable : 1 - Expérimental]
Stable : 1 Stabilité : 1 - Expérimental
- Type : <string>
{
"packageManager": "<nom du gestionnaire de paquets>@<version>"
}
Le champ "packageManager"
définit quel gestionnaire de paquets est censé être utilisé lors du travail sur le projet actuel. Il peut être défini sur n'importe quel gestionnaire de paquets pris en charge, et garantira que vos équipes utilisent exactement les mêmes versions de gestionnaire de paquets sans avoir à installer autre chose que Node.js.
Ce champ est actuellement expérimental et nécessite une activation ; consultez la page Corepack pour obtenir des détails sur la procédure.
"type"
[Historique]
Version | Changements |
---|---|
v13.2.0, v12.17.0 | Suppression du drapeau --experimental-modules . |
v12.0.0 | Ajouté dans : v12.0.0 |
- Type : <string>
Le champ "type"
définit le format de module que Node.js utilise pour tous les fichiers .js
dont le fichier package.json
le plus proche est le parent.
Les fichiers se terminant par .js
sont chargés en tant que modules ES lorsque le fichier package.json
parent le plus proche contient un champ de premier niveau "type"
avec la valeur "module"
.
Le package.json
parent le plus proche est défini comme le premier package.json
trouvé lors de la recherche dans le dossier actuel, le dossier parent de ce dossier, et ainsi de suite jusqu'à ce qu'un dossier node_modules ou la racine du volume soit atteinte.
// package.json
{
"type": "module"
}
# Dans le même dossier que le package.json précédent {#in-same-folder-as-preceding-packagejson}
node my-app.js # S'exécute en tant que module ES
Si le package.json
parent le plus proche n'a pas de champ "type"
, ou contient "type": "commonjs"
, les fichiers .js
sont traités comme CommonJS. Si la racine du volume est atteinte et qu'aucun package.json
n'est trouvé, les fichiers .js
sont traités comme CommonJS.
Les instructions import
des fichiers .js
sont traitées comme des modules ES si le package.json
parent le plus proche contient "type": "module"
.
// my-app.js, partie du même exemple que ci-dessus
import './startup.js' // Chargé comme module ES en raison du package.json
Quelle que soit la valeur du champ "type"
, les fichiers .mjs
sont toujours traités comme des modules ES et les fichiers .cjs
sont toujours traités comme CommonJS.
"exports"
[Historique]
Version | Changements |
---|---|
v14.13.0, v12.20.0 | Ajout de la prise en charge des modèles "exports" . |
v13.7.0, v12.17.0 | Suppression du drapeau des exportations conditionnelles. |
v13.7.0, v12.16.0 | Mise en œuvre du classement logique des exportations conditionnelles. |
v13.7.0, v12.16.0 | Suppression de l'option --experimental-conditional-exports . Dans la version 12.16.0, les exportations conditionnelles sont toujours derrière --experimental-modules . |
v13.2.0, v12.16.0 | Mise en œuvre des exportations conditionnelles. |
v12.7.0 | Ajouté dans : v12.7.0 |
- Type : <Object> | <string> | <string[]>
{
"exports": "./index.js"
}
Le champ "exports"
permet de définir les points d’entrée d’un package lorsqu’il est importé par son nom, chargé via une recherche node_modules
ou une autoréférence à son propre nom. Il est pris en charge dans Node.js 12 et plus comme une alternative à "main"
qui peut prendre en charge la définition des exportations de sous-chemin et des exportations conditionnelles tout en encapsulant les modules internes non exportés.
Les exportations conditionnelles peuvent également être utilisées dans "exports"
pour définir différents points d’entrée de package par environnement, y compris si le package est référencé via require
ou via import
.
Tous les chemins définis dans "exports"
doivent être des URL de fichiers relatives commençant par ./
.
"imports"
Ajouté dans : v14.6.0, v12.19.0
- Type : <Object>
// package.json
{
"imports": {
"#dep": {
"node": "dep-node-native",
"default": "./dep-polyfill.js"
}
},
"dependencies": {
"dep-node-native": "^1.0.0"
}
}
Les entrées dans le champ imports
doivent être des chaînes commençant par #
.
Les importations de packages permettent de faire correspondre à des packages externes.
Ce champ définit les importations de sous-chemin pour le package actuel.