Moduli: Pacchetti
[Cronologia]
Versione | Modifiche |
---|---|
v14.13.0, v12.20.0 | Aggiunto il supporto per i pattern "exports" . |
v14.6.0, v12.19.0 | Aggiunto il campo "imports" del pacchetto. |
v13.7.0, v12.17.0 | Rimosse le esportazioni condizionali. |
v13.7.0, v12.16.0 | Rimossa l'opzione --experimental-conditional-exports . Nella 12.16.0, le esportazioni condizionali sono ancora dietro a --experimental-modules . |
v13.6.0, v12.16.0 | Rimosso il flag di auto-referenziazione di un pacchetto usando il suo nome. |
v12.7.0 | Introdotto il campo "exports" in package.json come alternativa più potente al classico campo "main" . |
v12.0.0 | Aggiunto il supporto per i moduli ES usando l'estensione file .js tramite il campo "type" in package.json . |
Introduzione
Un pacchetto è una struttura ad albero di cartelle descritta da un file package.json
. Il pacchetto è costituito dalla cartella contenente il file package.json
e tutte le sottocartelle fino alla successiva cartella contenente un altro file package.json
o una cartella denominata node_modules
.
Questa pagina fornisce una guida per gli autori di pacchetti che scrivono file package.json
, insieme a un riferimento per i campi package.json
definiti da Node.js.
Determinazione del sistema di moduli
Introduzione
Node.js tratterà quanto segue come moduli ES quando passato a node
come input iniziale, o quando referenziato da istruzioni import
o espressioni import()
:
- File con estensione
.mjs
. - File con estensione
.js
quando il filepackage.json
padre più vicino contiene un campo di primo livello"type"
con un valore di"module"
. - Stringhe passate come argomento a
--eval
, o inviate anode
tramiteSTDIN
, con il flag--input-type=module
. - Codice contenente una sintassi analizzata con successo solo come moduli ES, come istruzioni
import
oexport
oimport.meta
, senza un indicatore esplicito di come dovrebbe essere interpretato. Gli indicatori espliciti sono le estensioni.mjs
o.cjs
, i campi"type"
dipackage.json
con valori"module"
o"commonjs"
, o il flag--input-type
. Le espressioni dinamicheimport()
sono supportate sia nei moduli CommonJS che ES e non forzerebbero un file a essere trattato come un modulo ES. Vedi Rilevamento della sintassi.
Node.js tratterà quanto segue come CommonJS quando passato a node
come input iniziale, o quando referenziato da istruzioni import
o espressioni import()
:
- File con estensione
.cjs
. - File con estensione
.js
quando il filepackage.json
padre più vicino contiene un campo di primo livello"type"
con un valore di"commonjs"
. - Stringhe passate come argomento a
--eval
o--print
, o inviate anode
tramiteSTDIN
, con il flag--input-type=commonjs
. - File con estensione
.js
senza un filepackage.json
padre o dove il filepackage.json
padre più vicino non ha un campotype
, e dove il codice può essere valutato con successo come CommonJS. In altre parole, Node.js prova prima a eseguire tali file "ambigui" come CommonJS e riproverà a valutarli come moduli ES se la valutazione come CommonJS fallisce perché il parser ha trovato la sintassi del modulo ES.
Scrivere la sintassi del modulo ES in file "ambigui" comporta un costo in termini di prestazioni, e pertanto si consiglia agli autori di essere espliciti ove possibile. In particolare, gli autori dei pacchetti dovrebbero sempre includere il campo "type"
nei loro file package.json
, anche nei pacchetti in cui tutte le fonti sono CommonJS. Essere espliciti sul type
del pacchetto metterà al sicuro il pacchetto nel caso in cui il tipo predefinito di Node.js cambi, e renderà anche più facile per gli strumenti di build e i loader determinare come i file nel pacchetto dovrebbero essere interpretati.
Rilevamento della sintassi
[Cronologia]
Versione | Modifiche |
---|---|
v22.7.0 | Il rilevamento della sintassi è abilitato di default. |
v21.1.0, v20.10.0 | Aggiunto in: v21.1.0, v20.10.0 |
[Stabile: 1 - Sperimentale]
Stabile: 1 Stabilità: 1.2 - Release candidate
Node.js ispezionerà il codice sorgente di input ambiguo per determinare se contiene la sintassi del modulo ES; se tale sintassi viene rilevata, l'input verrà trattato come un modulo ES.
L'input ambiguo è definito come:
- File con estensione
.js
o senza estensione; e nessun filepackage.json
di controllo o uno che manca di un campotype
. - Input di stringhe (
--eval
oSTDIN
) quando--input-type
non è specificato.
La sintassi del modulo ES è definita come sintassi che verrebbe lanciata se valutata come CommonJS. Ciò include quanto segue:
- Istruzioni
import
(ma non espressioniimport()
, che sono valide in CommonJS). - Istruzioni
export
. - Riferimenti
import.meta
. await
al livello superiore di un modulo.- Ridefinizioni lessicali delle variabili wrapper CommonJS (
require
,module
,exports
,__dirname
,__filename
).
Caricatori di moduli
Node.js ha due sistemi per risolvere uno specificatore e caricare i moduli.
C'è il caricatore di moduli CommonJS:
- È completamente sincrono.
- È responsabile della gestione delle chiamate
require()
. - È modificabile tramite monkey patch.
- Supporta le cartelle come moduli.
- Quando si risolve uno specificatore, se non viene trovata una corrispondenza esatta, tenterà di aggiungere estensioni (
.js
,.json
e infine.node
) e quindi tenterà di risolvere cartelle come moduli. - Considera
.json
come file di testo JSON. - I file
.node
vengono interpretati come moduli addon compilati caricati conprocess.dlopen()
. - Considera tutti i file privi di estensione
.json
o.node
come file di testo JavaScript. - Può essere utilizzato solo per caricare moduli ECMAScript da moduli CommonJS se il grafico dei moduli è sincrono (ovvero non contiene
await
di livello superiore). Se utilizzato per caricare un file di testo JavaScript che non è un modulo ECMAScript, il file verrà caricato come modulo CommonJS.
C'è il caricatore di moduli ECMAScript:
- È asincrono, a meno che non venga utilizzato per caricare moduli per
require()
. - È responsabile della gestione delle istruzioni
import
e delle espressioniimport()
. - Non è modificabile tramite monkey patch, può essere personalizzato utilizzando gli hook del caricatore.
- Non supporta le cartelle come moduli, gli indici di directory (ad es.
'./startup/index.js'
) devono essere specificati completamente. - Non effettua ricerche di estensioni. Un'estensione di file deve essere fornita quando lo specificatore è un URL di file relativo o assoluto.
- Può caricare moduli JSON, ma è richiesto un attributo del tipo di importazione.
- Accetta solo estensioni
.js
,.mjs
e.cjs
per file di testo JavaScript. - Può essere utilizzato per caricare moduli JavaScript CommonJS. Tali moduli vengono passati attraverso
cjs-module-lexer
per cercare di identificare le esportazioni con nome, che sono disponibili se possono essere determinate tramite analisi statica. I moduli CommonJS importati hanno i loro URL convertiti in percorsi assoluti e vengono quindi caricati tramite il caricatore di moduli CommonJS.
package.json
ed estensioni dei file
All'interno di un package, il campo "type"
del package.json
definisce come Node.js deve interpretare i file .js
. Se un file package.json
non ha un campo "type"
, i file .js
vengono trattati come CommonJS.
Un valore "type"
di "module"
nel package.json
indica a Node.js di interpretare i file .js
all'interno di quel package come se utilizzassero la sintassi ES module.
Il campo "type"
si applica non solo ai punti di ingresso iniziali (node my-app.js
) ma anche ai file a cui si fa riferimento tramite istruzioni import
ed espressioni import()
.
// my-app.js, trattato come un ES module perché esiste un file package.json
// nella stessa cartella con "type": "module".
import './startup/init.js'
// Caricato come ES module poiché ./startup non contiene un file package.json,
// e quindi eredita il valore "type" dal livello superiore.
import 'commonjs-package'
// Caricato come CommonJS poiché ./node_modules/commonjs-package/package.json
// non ha un campo "type" o contiene "type": "commonjs".
import './node_modules/commonjs-package/index.js'
// Caricato come CommonJS poiché ./node_modules/commonjs-package/package.json
// non ha un campo "type" o contiene "type": "commonjs".
I file che terminano con .mjs
vengono sempre caricati come ES module indipendentemente dal package.json
principale più vicino.
I file che terminano con .cjs
vengono sempre caricati come CommonJS indipendentemente dal package.json
principale più vicino.
import './legacy-file.cjs'
// Caricato come CommonJS poiché .cjs viene sempre caricato come CommonJS.
import 'commonjs-package/src/index.mjs'
// Caricato come ES module poiché .mjs viene sempre caricato come ES module.
Le estensioni .mjs
e .cjs
possono essere utilizzate per combinare i tipi all'interno dello stesso package:
- All'interno di un package con
"type": "module"
, Node.js può essere istruito per interpretare un determinato file come CommonJS nominandolo con un'estensione.cjs
(poiché sia i file.js
che.mjs
vengono trattati come ES module all'interno di un package con"module"
). - All'interno di un package con
"type": "commonjs"
, Node.js può essere istruito per interpretare un determinato file come ES module nominandolo con un'estensione.mjs
(poiché sia i file.js
che.cjs
vengono trattati come CommonJS all'interno di un package con"commonjs"
).
Flag --input-type
Aggiunto in: v12.0.0
Le stringhe passate come argomento a --eval
(o -e
), o inviate tramite pipe a node
via STDIN
, sono trattate come moduli ES quando è impostato il flag --input-type=module
.
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
Per completezza, esiste anche --input-type=commonjs
, per eseguire esplicitamente l'input di stringa come CommonJS. Questo è il comportamento predefinito se --input-type
non è specificato.
Determinare il gestore di pacchetti
[Stabile: 1 - Sperimentale]
Stabile: 1 Stabilità: 1 - Sperimentale
Sebbene ci si aspetti che tutti i progetti Node.js siano installabili da tutti i gestori di pacchetti una volta pubblicati, i loro team di sviluppo sono spesso tenuti a utilizzare uno specifico gestore di pacchetti. Per semplificare questo processo, Node.js viene fornito con uno strumento chiamato Corepack che mira a rendere tutti i gestori di pacchetti trasparentemente disponibili nel tuo ambiente, a condizione che tu abbia installato Node.js.
Per impostazione predefinita, Corepack non applicherà alcun gestore di pacchetti specifico e utilizzerà le versioni generiche "Last Known Good" associate a ogni versione di Node.js, ma puoi migliorare questa esperienza impostando il campo "packageManager"
nel package.json
del tuo progetto.
Punti di ingresso del pacchetto
Nel file package.json
di un pacchetto, due campi possono definire i punti di ingresso per un pacchetto: "main"
e "exports"
. Entrambi i campi si applicano sia ai punti di ingresso del modulo ES che del modulo CommonJS.
Il campo "main"
è supportato in tutte le versioni di Node.js, ma le sue capacità sono limitate: definisce solo il punto di ingresso principale del pacchetto.
Il campo "exports"
fornisce un'alternativa moderna a "main"
consentendo di definire più punti di ingresso, il supporto della risoluzione dell'ingresso condizionale tra gli ambienti e impedendo qualsiasi altro punto di ingresso oltre a quelli definiti in "exports"
. Questa incapsulamento consente agli autori di moduli di definire chiaramente l'interfaccia pubblica per il loro pacchetto.
Per i nuovi pacchetti destinati alle versioni attualmente supportate di Node.js, si consiglia il campo "exports"
. Per i pacchetti che supportano Node.js 10 e versioni precedenti, è richiesto il campo "main"
. Se sono definiti sia "exports"
che "main"
, il campo "exports"
ha la precedenza su "main"
nelle versioni supportate di Node.js.
Le esportazioni condizionali possono essere utilizzate all'interno di "exports"
per definire diversi punti di ingresso del pacchetto per ambiente, incluso se il pacchetto viene referenziato tramite require
o tramite import
. Per maggiori informazioni sul supporto di moduli CommonJS ed ES in un singolo pacchetto, si prega di consultare la sezione pacchetti a doppio modulo CommonJS/ES.
I pacchetti esistenti che introducono il campo "exports"
impediranno ai consumatori del pacchetto di utilizzare qualsiasi punto di ingresso non definito, incluso il package.json
(ad es. require('your-package/package.json')
). Questo sarà probabilmente una modifica che causa interruzioni.
Per rendere non interruttiva l'introduzione di "exports"
, assicurarsi che ogni punto di ingresso precedentemente supportato venga esportato. È meglio specificare esplicitamente i punti di ingresso in modo che l'API pubblica del pacchetto sia ben definita. Ad esempio, un progetto che in precedenza esportava main
, lib
, feature
e package.json
potrebbe utilizzare il seguente package.exports
:
{
"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"
}
}
In alternativa, un progetto potrebbe scegliere di esportare intere cartelle sia con che senza sottopath estesi utilizzando modelli di esportazione:
{
"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"
}
}
Con quanto sopra che fornisce la retrocompatibilità per qualsiasi versione minore del pacchetto, una futura modifica importante per il pacchetto può quindi limitare correttamente le esportazioni solo alle esportazioni specifiche delle funzionalità esposte:
{
"name": "my-package",
"exports": {
".": "./lib/index.js",
"./feature/*.js": "./feature/*.js",
"./feature/internal/*": null
}
}
Punto di ingresso principale per l'esportazione
Quando si scrive un nuovo pacchetto, si raccomanda di utilizzare il campo "exports"
:
{
"exports": "./index.js"
}
Quando il campo "exports"
è definito, tutti i sottocammini del pacchetto sono incapsulati e non più disponibili per gli importatori. Per esempio, require('pkg/subpath.js')
genera un errore ERR_PACKAGE_PATH_NOT_EXPORTED
.
Questa incapsulamento delle esportazioni fornisce garanzie più affidabili sulle interfacce dei pacchetti per gli strumenti e quando si gestiscono gli aggiornamenti semver per un pacchetto. Non è un incapsulamento forte, poiché un require
diretto di qualsiasi sottocammino assoluto del pacchetto come require('/path/to/node_modules/pkg/subpath.js')
caricherà comunque subpath.js
.
Tutte le versioni attualmente supportate di Node.js e gli strumenti di build moderni supportano il campo "exports"
. Per i progetti che utilizzano una versione precedente di Node.js o un relativo strumento di build, la compatibilità può essere raggiunta includendo il campo "main"
insieme a "exports"
che punta allo stesso modulo:
{
"main": "./index.js",
"exports": "./index.js"
}
Esportazione di sottocammini
Aggiunto in: v12.7.0
Quando si utilizza il campo "exports"
, i sottocammini personalizzati possono essere definiti insieme al punto di ingresso principale trattando il punto di ingresso principale come il sottocammino "."
:
{
"exports": {
".": "./index.js",
"./submodule.js": "./src/submodule.js"
}
}
Ora solo il sottocammino definito in "exports"
può essere importato da un consumatore:
import submodule from 'es-module-package/submodule.js'
// Carica ./node_modules/es-module-package/src/submodule.js
Mentre altri sottocammini genereranno errori:
import submodule from 'es-module-package/private-module.js'
// Genera ERR_PACKAGE_PATH_NOT_EXPORTED
Estensioni nei sottocammini
Gli autori dei pacchetti dovrebbero fornire sottocammini con estensione (import 'pkg/subpath.js'
) o senza estensione (import 'pkg/subpath'
) nelle loro esportazioni. Ciò garantisce che ci sia un solo sottocammino per ogni modulo esportato, in modo che tutti i dipendenti importino lo stesso specificatore coerente, mantenendo chiaro il contratto del pacchetto per i consumatori e semplificando i completamenti dei sottocammini del pacchetto.
Tradizionalmente, i pacchetti tendevano a utilizzare lo stile senza estensione, che ha i vantaggi della leggibilità e del mascheramento del vero percorso del file all'interno del pacchetto.
Con le mappe di importazione che ora forniscono uno standard per la risoluzione dei pacchetti nei browser e in altri runtime JavaScript, l'utilizzo dello stile senza estensione può comportare definizioni di mappe di importazione gonfie. Le estensioni di file esplicite possono evitare questo problema consentendo alla mappa di importazione di utilizzare una mappatura della cartella dei pacchetti per mappare più sottocammini dove possibile invece di una voce di mappa separata per ciascuna esportazione di sottocammino del pacchetto. Questo rispecchia anche il requisito di utilizzare il percorso completo dello specificatore negli specificatori di importazione relativi e assoluti.
Esportazioni semplificate
Aggiunto in: v12.11.0
Se l'esportazione "."
è l'unica esportazione, il campo "exports"
fornisce una scorciatoia per questo caso, essendo il valore diretto del campo "exports"
.
{
"exports": {
".": "./index.js"
}
}
può essere scritto:
{
"exports": "./index.js"
}
Importazioni di sottopath
Aggiunto in: v14.6.0, v12.19.0
Oltre al campo "exports"
, esiste un campo "imports"
del pacchetto per creare mappature private che si applicano solo agli specificatori di importazione dall'interno del pacchetto stesso.
Le voci nel campo "imports"
devono sempre iniziare con #
per garantire che siano disambiguate dagli specificatori di pacchetti esterni.
Ad esempio, il campo imports può essere utilizzato per ottenere i vantaggi delle esportazioni condizionali per i moduli interni:
// package.json
{
"imports": {
"#dep": {
"node": "dep-node-native",
"default": "./dep-polyfill.js"
}
},
"dependencies": {
"dep-node-native": "^1.0.0"
}
}
dove import '#dep'
non ottiene la risoluzione del pacchetto esterno dep-node-native
(incluse le sue esportazioni a sua volta) e invece ottiene il file locale ./dep-polyfill.js
relativo al pacchetto in altri ambienti.
A differenza del campo "exports"
, il campo "imports"
consente la mappatura a pacchetti esterni.
Le regole di risoluzione per il campo imports sono altrimenti analoghe al campo exports.
Modelli di sottopath
[Cronologia]
Versione | Modifiche |
---|---|
v16.10.0, v14.19.0 | Supporto per i trailer di pattern nel campo "imports". |
v16.9.0, v14.19.0 | Supporto per i trailer di pattern. |
v14.13.0, v12.20.0 | Aggiunto in: v14.13.0, v12.20.0 |
Per i pacchetti con un piccolo numero di esportazioni o importazioni, si consiglia di elencare esplicitamente ciascuna voce di sottopath di esportazione. Ma per i pacchetti che hanno un gran numero di sottopath, ciò potrebbe causare un'eccessiva espansione del file package.json
e problemi di manutenzione.
Per questi casi d'uso, è possibile utilizzare invece pattern di esportazione di sottopath:
// ./node_modules/es-module-package/package.json
{
"exports": {
"./features/*.js": "./src/features/*.js"
},
"imports": {
"#internal/*.js": "./src/internal/*.js"
}
}
*
le mappe espongono i sottopath nidificati in quanto è solo una sintassi di sostituzione di stringhe.
Tutte le istanze di *
sul lato destro verranno quindi sostituite con questo valore, anche se contiene separatori /
.
import featureX from 'es-module-package/features/x.js'
// Carica ./node_modules/es-module-package/src/features/x.js
import featureY from 'es-module-package/features/y/y.js'
// Carica ./node_modules/es-module-package/src/features/y/y.js
import internalZ from '#internal/z.js'
// Carica ./node_modules/es-module-package/src/internal/z.js
Questa è una corrispondenza e sostituzione statica diretta senza alcuna gestione speciale per le estensioni di file. Includere "*.js"
su entrambi i lati della mappatura limita le esportazioni di pacchetti esposte solo ai file JS.
La proprietà delle esportazioni che possono essere staticamente enumerate viene mantenuta con i pattern di esportazione poiché le singole esportazioni per un pacchetto possono essere determinate trattando il pattern di destinazione sul lato destro come un glob **
rispetto all'elenco di file all'interno del pacchetto. Poiché i percorsi node_modules
sono vietati nelle destinazioni di esportazione, questa espansione dipende solo dai file del pacchetto stesso.
Per escludere sottocartelle private dai pattern, è possibile utilizzare destinazioni null
:
// ./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'
// Restituisce: ERR_PACKAGE_PATH_NOT_EXPORTED
import featureX from 'es-module-package/features/x.js'
// Carica ./node_modules/es-module-package/src/features/x.js
Esportazioni condizionali
[Cronologia]
Versione | Modifiche |
---|---|
v13.7.0, v12.16.0 | Rimosse le flag per le esportazioni condizionali. |
v13.2.0, v12.16.0 | Aggiunte in: v13.2.0, v12.16.0 |
Le esportazioni condizionali forniscono un modo per mappare a percorsi diversi a seconda di determinate condizioni. Sono supportate sia per le importazioni CommonJS che per i moduli ES.
Ad esempio, un pacchetto che desidera fornire esportazioni di moduli ES diversi per require()
e import
può essere scritto come:
// package.json
{
"exports": {
"import": "./index-module.js",
"require": "./index-require.cjs"
},
"type": "module"
}
Node.js implementa le seguenti condizioni, elencate in ordine dalla più specifica alla meno specifica, poiché le condizioni dovrebbero essere definite:
"node-addons"
- simile a"node"
e corrisponde a qualsiasi ambiente Node.js. Questa condizione può essere utilizzata per fornire un punto di ingresso che utilizza add-on C++ nativi in contrapposizione a un punto di ingresso più universale e che non si basa su add-on nativi. Questa condizione può essere disabilitata tramite il flag--no-addons
."node"
- corrisponde a qualsiasi ambiente Node.js. Può essere un file CommonJS o un modulo ES. Nella maggior parte dei casi, non è necessario specificare esplicitamente la piattaforma Node.js."import"
- corrisponde quando il pacchetto viene caricato tramiteimport
oimport()
, o tramite qualsiasi importazione di primo livello o operazione di risoluzione dal caricatore di moduli ECMAScript. Si applica indipendentemente dal formato del modulo del file di destinazione. Sempre mutualmente esclusiva con"require"
."require"
- corrisponde quando il pacchetto viene caricato tramiterequire()
. Il file di riferimento dovrebbe essere caricabile conrequire()
, sebbene la condizione corrisponda indipendentemente dal formato del modulo del file di destinazione. I formati previsti includono CommonJS, JSON, add-on nativi e moduli ES. Sempre mutualmente esclusiva con"import"
."module-sync"
- corrisponde indipendentemente dal fatto che il pacchetto venga caricato tramiteimport
,import()
orequire()
. Il formato previsto è quello dei moduli ES che non contengono await di primo livello nel suo grafo di moduli: in caso contrario, verrà generatoERR_REQUIRE_ASYNC_MODULE
quando il modulo vienerequire()
-ed."default"
- il fallback generico che corrisponde sempre. Può essere un file CommonJS o un modulo ES. Questa condizione dovrebbe sempre essere l'ultima.
All'interno dell'oggetto "exports"
, l'ordine delle chiavi è significativo. Durante la corrispondenza delle condizioni, le voci precedenti hanno una priorità maggiore e hanno la precedenza sulle voci successive. La regola generale è che le condizioni dovrebbero andare dalla più specifica alla meno specifica nell'ordine dell'oggetto.
L'utilizzo delle condizioni "import"
e "require"
può portare ad alcuni rischi, che sono ulteriormente spiegati nella sezione sui pacchetti duali CommonJS/moduli ES.
La condizione "node-addons"
può essere utilizzata per fornire un punto di ingresso che utilizza add-on C++ nativi. Tuttavia, questa condizione può essere disabilitata tramite il flag --no-addons
. Quando si utilizza "node-addons"
, si consiglia di trattare "default"
come un miglioramento che fornisce un punto di ingresso più universale, ad es. utilizzando WebAssembly anziché un add-on nativo.
Le esportazioni condizionali possono essere estese anche ai sotto-percorsi di esportazione, ad esempio:
{
"exports": {
".": "./index.js",
"./feature.js": {
"node": "./feature-node.js",
"default": "./feature.js"
}
}
}
Definisce un pacchetto in cui require('pkg/feature.js')
e import 'pkg/feature.js'
potrebbero fornire implementazioni diverse tra Node.js e altri ambienti JS.
Quando si utilizzano rami di ambiente, includere sempre una condizione "default"
quando possibile. Fornire una condizione "default"
garantisce che qualsiasi ambiente JS sconosciuto sia in grado di utilizzare questa implementazione universale, il che aiuta a evitare che questi ambienti JS debbano fingere di essere ambienti esistenti per supportare pacchetti con esportazioni condizionali. Per questo motivo, l'utilizzo dei rami di condizione "node"
e "default"
è generalmente preferibile all'utilizzo dei rami di condizione "node"
e "browser"
.
Condizioni annidate
Oltre ai mapping diretti, Node.js supporta anche oggetti di condizione annidati.
Ad esempio, per definire un pacchetto che ha solo punti di ingresso in modalità duale per l'uso in Node.js ma non nel browser:
{
"exports": {
"node": {
"import": "./feature-node.mjs",
"require": "./feature-node.cjs"
},
"default": "./feature.mjs"
}
}
Le condizioni continuano a essere corrisposte in ordine come con le condizioni piatte. Se una condizione nidificata non ha alcun mapping, continuerà a controllare le condizioni rimanenti della condizione padre. In questo modo le condizioni nidificate si comportano in modo analogo alle istruzioni if
JavaScript nidificate.
Risoluzione delle condizioni utente
Aggiunto in: v14.9.0, v12.19.0
Quando si esegue Node.js, le condizioni utente personalizzate possono essere aggiunte con il flag --conditions
:
node --conditions=development index.js
che quindi risolverebbe la condizione "development"
nelle importazioni e nelle esportazioni di pacchetti, risolvendo allo stesso tempo le condizioni esistenti "node"
, "node-addons"
, "default"
, "import"
e "require"
in modo appropriato.
Un numero qualsiasi di condizioni personalizzate può essere impostato con flag ripetuti.
Le condizioni tipiche dovrebbero contenere solo caratteri alfanumerici, utilizzando ":", "-" o "=" come separatori, se necessario. Qualsiasi altra cosa potrebbe incorrere in problemi di compatibilità al di fuori di Node.
In Node, le condizioni hanno pochissime restrizioni, ma in particolare queste includono:
Definizioni delle condizioni della community
Le stringhe di condizione diverse dalle condizioni "import"
, "require"
, "node"
, "module-sync"
, "node-addons"
e "default"
implementate nel core di Node.js vengono ignorate di default.
Altre piattaforme potrebbero implementare altre condizioni e le condizioni utente possono essere abilitate in Node.js tramite il flag --conditions
/ -C
.
Poiché le condizioni di pacchetto personalizzate richiedono definizioni chiare per garantire un uso corretto, viene fornito di seguito un elenco di condizioni di pacchetto comuni note e le loro definizioni rigorose per aiutare il coordinamento dell'ecosistema.
"types"
- può essere utilizzato dai sistemi di tipizzazione per risolvere il file di tipizzazione per l'esportazione indicata. Questa condizione dovrebbe essere sempre inclusa per prima."browser"
- qualsiasi ambiente browser web."development"
- può essere utilizzato per definire un punto di ingresso per l'ambiente di solo sviluppo, ad esempio per fornire un contesto di debug aggiuntivo come messaggi di errore migliori quando si esegue in modalità di sviluppo. Deve essere sempre mutuamente esclusivo con"production"
."production"
- può essere utilizzato per definire un punto di ingresso per l'ambiente di produzione. Deve essere sempre mutuamente esclusivo con"development"
.
Per altri runtime, le definizioni delle chiavi di condizione specifiche della piattaforma sono gestite dal WinterCG nella specifica della proposta Runtime Keys.
Nuove definizioni di condizioni possono essere aggiunte a questo elenco creando una pull request alla documentazione di Node.js per questa sezione. I requisiti per l'inserimento di una nuova definizione di condizione qui sono che:
- La definizione deve essere chiara e non ambigua per tutti gli implementatori.
- Il caso d'uso per cui la condizione è necessaria deve essere chiaramente giustificato.
- Dovrebbe esistere un utilizzo sufficiente dell'implementazione esistente.
- Il nome della condizione non deve essere in conflitto con un'altra definizione di condizione o condizione in ampio uso.
- L'elenco della definizione di condizione dovrebbe fornire un vantaggio di coordinamento all'ecosistema che altrimenti non sarebbe possibile. Ad esempio, questo non sarebbe necessariamente il caso per le condizioni specifiche dell'azienda o dell'applicazione.
- La condizione dovrebbe essere tale che un utente di Node.js si aspetterebbe che fosse nella documentazione principale di Node.js. La condizione
"types"
è un buon esempio: non appartiene realmente alla proposta Runtime Keys ma è adatta qui nella documentazione di Node.js.
Le definizioni di cui sopra possono essere spostate in un registro di condizioni dedicato a tempo debito.
Riferimento automatico a un package usando il suo nome
[Cronologia]
Versione | Modifiche |
---|---|
v13.6.0, v12.16.0 | Rimosso il flag dal riferimento automatico a un package usando il suo nome. |
v13.1.0, v12.16.0 | Aggiunto in: v13.1.0, v12.16.0 |
All'interno di un package, i valori definiti nel campo "exports"
del package.json
del package possono essere referenziati tramite il nome del package. Ad esempio, supponendo che il package.json
sia:
// package.json
{
"name": "a-package",
"exports": {
".": "./index.mjs",
"./foo.js": "./foo.js"
}
}
Quindi, qualsiasi modulo in quel package può referenziare un export all'interno del package stesso:
// ./a-module.mjs
import { something } from 'a-package' // Importa "something" da ./index.mjs.
Il riferimento automatico è disponibile solo se package.json
ha "exports"
, e consentirà l'importazione solo di ciò che "exports"
(nel package.json
) consente. Quindi il codice seguente, dato il package precedente, genererà un errore a runtime:
// ./another-module.mjs
// Importa "another" da ./m.mjs. Fallisce perché
// il campo "exports" di "package.json"
// non fornisce un export chiamato "./m.mjs".
import { another } from 'a-package/m.mjs'
Il riferimento automatico è disponibile anche quando si usa require
, sia in un modulo ES che in uno CommonJS. Ad esempio, anche questo codice funzionerà:
// ./a-module.js
const { something } = require('a-package/foo.js') // Carica da ./foo.js.
Infine, il riferimento automatico funziona anche con i package con scope. Ad esempio, anche questo codice funzionerà:
// package.json
{
"name": "@my/package",
"exports": "./index.js"
}
// ./index.js
module.exports = 42
// ./other.js
console.log(require('@my/package'))
$ node other.js
42
Package duali CommonJS/ES module
Vedi il repository degli esempi di package per i dettagli.
Definizioni dei campi package.json
di Node.js
Questa sezione descrive i campi usati dal runtime di Node.js. Altri strumenti (come npm) usano campi aggiuntivi che sono ignorati da Node.js e non documentati qui.
I seguenti campi nei file package.json
sono usati in Node.js:
"name"
- Rilevante quando si usano importazioni con nome all'interno di un package. Usato anche dai gestori di package come nome del package."main"
- Il modulo predefinito quando si carica il package, se exports non è specificato e nelle versioni di Node.js precedenti all'introduzione di exports."packageManager"
- Il gestore di package consigliato quando si contribuisce al package. Sfruttato dagli shim di Corepack."type"
- Il tipo di package che determina se caricare i file.js
come CommonJS o moduli ES."exports"
- Export del package ed export condizionali. Quando presente, limita quali sottomoduli possono essere caricati dall'interno del package."imports"
- Import del package, per l'uso da parte dei moduli all'interno del package stesso.
"name"
[Cronologia]
Versione | Cambiamenti |
---|---|
v13.6.0, v12.16.0 | Rimosssa l'opzione --experimental-resolve-self . |
v13.1.0, v12.16.0 | Aggiunto in: v13.1.0, v12.16.0 |
- Tipo: <stringa>
{
"name": "nome-pacchetto"
}
Il campo "name"
definisce il nome del tuo pacchetto. La pubblicazione nel registro npm richiede un nome che soddisfi determinati requisiti.
Il campo "name"
può essere utilizzato in aggiunta al campo "exports"
per fare riferimento a se stesso ad un pacchetto utilizzando il suo nome.
"main"
Aggiunto in: v0.4.0
- Tipo: <stringa>
{
"main": "./index.js"
}
Il campo "main"
definisce il punto di ingresso di un pacchetto quando viene importato per nome tramite una ricerca node_modules
. Il suo valore è un percorso.
Quando un pacchetto ha un campo "exports"
, questo avrà la precedenza sul campo "main"
quando si importa il pacchetto per nome.
Definisce anche lo script che viene utilizzato quando la directory del pacchetto viene caricata tramite require()
.
// Questo si risolve in ./path/to/directory/index.js.
require('./path/to/directory')
"packageManager"
Aggiunto in: v16.9.0, v14.19.0
[Stabile: 1 - Sperimentale]
Stabile: 1 Stabilità: 1 - Sperimentale
- Tipo: <stringa>
{
"packageManager": "<nome del gestore pacchetti>@<versione>"
}
Il campo "packageManager"
definisce quale gestore di pacchetti dovrebbe essere utilizzato quando si lavora al progetto corrente. Può essere impostato su uno qualsiasi dei gestori di pacchetti supportati e garantirà che i tuoi team utilizzino le stesse versioni del gestore di pacchetti senza dover installare nient'altro oltre a Node.js.
Questo campo è attualmente sperimentale e deve essere attivato; controlla la pagina Corepack per i dettagli sulla procedura.
"type"
[Cronologia]
Versione | Cambiamenti |
---|---|
v13.2.0, v12.17.0 | Rimosso il flag --experimental-modules . |
v12.0.0 | Aggiunto in: v12.0.0 |
- Tipo: <stringa>
Il campo "type"
definisce il formato del modulo che Node.js utilizza per tutti i file .js
che hanno quel file package.json
come il loro genitore più vicino.
I file che terminano con .js
vengono caricati come moduli ES quando il file package.json
genitore più vicino contiene un campo di livello superiore "type"
con valore "module"
.
Il package.json
genitore più vicino è definito come il primo package.json
trovato durante la ricerca nella cartella corrente, nella cartella genitore di quella cartella e così via fino a quando non viene raggiunta una cartella node_modules o la radice del volume.
// package.json
{
"type": "module"
}
# Nella stessa cartella del precedente package.json {#in-same-folder-as-preceding-packagejson}
node my-app.js # Viene eseguito come modulo ES
Se il package.json
genitore più vicino non ha un campo "type"
, o contiene "type": "commonjs"
, i file .js
vengono trattati come CommonJS. Se viene raggiunta la radice del volume e non viene trovato alcun package.json
, i file .js
vengono trattati come CommonJS.
Le istruzioni import
dei file .js
vengono trattate come moduli ES se il package.json
genitore più vicino contiene "type": "module"
.
// my-app.js, parte dello stesso esempio di sopra
import './startup.js' // Caricato come modulo ES a causa di package.json
Indipendentemente dal valore del campo "type"
, i file .mjs
vengono sempre trattati come moduli ES e i file .cjs
vengono sempre trattati come CommonJS.
"exports"
[Cronologia]
Versione | Cambiamenti |
---|---|
v14.13.0, v12.20.0 | Aggiunto il supporto per i pattern "exports" . |
v13.7.0, v12.17.0 | Rimosso il flag degli export condizionali. |
v13.7.0, v12.16.0 | Implementato l'ordinamento logico degli export condizionali. |
v13.7.0, v12.16.0 | Rimosso l'opzione --experimental-conditional-exports . Nella versione 12.16.0, gli export condizionali sono ancora dietro --experimental-modules . |
v13.2.0, v12.16.0 | Implementati gli export condizionali. |
v12.7.0 | Aggiunto in: v12.7.0 |
- Tipo: <Oggetto> | <stringa> | <stringa[]>
{
"exports": "./index.js"
}
Il campo "exports"
consente di definire i punti di ingresso di un pacchetto quando importato per nome caricato tramite una ricerca node_modules
o un autoriferimento al suo nome. È supportato in Node.js 12+ come alternativa a "main"
che può supportare la definizione di esportazioni di sottopercorsi e esportazioni condizionali incapsulando al contempo i moduli interni non esportati.
Le Esportazioni Condizionali possono essere utilizzate anche all'interno di "exports"
per definire diversi punti di ingresso del pacchetto per ambiente, incluso se il pacchetto viene referenziato tramite require
o tramite import
.
Tutti i percorsi definiti in "exports"
devono essere URL di file relativi che iniziano con ./
.
"imports"
Aggiunto in: v14.6.0, v12.19.0
- Tipo: <Object>
// package.json
{
"imports": {
"#dep": {
"node": "dep-node-native",
"default": "./dep-polyfill.js"
}
},
"dependencies": {
"dep-node-native": "^1.0.0"
}
}
Le voci nel campo imports devono essere stringhe che iniziano con #
.
Le importazioni di pacchetto consentono la mappatura a pacchetti esterni.
Questo campo definisce le importazioni di sottopercorso per il pacchetto corrente.