Moduli: moduli CommonJS
[Stabile: 2 - Stabile]
Stabile: 2 Stabilità: 2 - Stabile
I moduli CommonJS sono il metodo originale per impacchettare il codice JavaScript per Node.js. Node.js supporta anche lo standard moduli ECMAScript utilizzato dai browser e da altri runtime JavaScript.
In Node.js, ogni file viene trattato come un modulo separato. Ad esempio, considera un file denominato foo.js
:
const circle = require('./circle.js')
console.log(`L'area di un cerchio di raggio 4 è ${circle.area(4)}`)
Nella prima riga, foo.js
carica il modulo circle.js
che si trova nella stessa directory di foo.js
.
Ecco il contenuto di circle.js
:
const { PI } = Math
exports.area = r => PI * r ** 2
exports.circumference = r => 2 * PI * r
Il modulo circle.js
ha esportato le funzioni area()
e circumference()
. Le funzioni e gli oggetti vengono aggiunti alla radice di un modulo specificando proprietà aggiuntive sull'oggetto speciale exports
.
Le variabili locali al modulo saranno private, perché il modulo è racchiuso in una funzione da Node.js (vedi wrapper del modulo). In questo esempio, la variabile PI
è privata per circle.js
.
La proprietà module.exports
può essere assegnata a un nuovo valore (come una funzione o un oggetto).
Nel codice seguente, bar.js
utilizza il modulo square
, che esporta una classe Square:
const Square = require('./square.js')
const mySquare = new Square(2)
console.log(`L'area del mio mySquare è ${mySquare.area()}`)
Il modulo square
è definito in square.js
:
// L'assegnazione a exports non modificherà il modulo, deve essere utilizzato module.exports
module.exports = class Square {
constructor(width) {
this.width = width
}
area() {
return this.width ** 2
}
}
Il sistema di moduli CommonJS è implementato nel module
modulo core.
Abilitazione
Node.js ha due sistemi di moduli: moduli CommonJS e moduli ECMAScript.
Per impostazione predefinita, Node.js tratterà i seguenti come moduli CommonJS:
- File con estensione
.cjs
; - File con estensione
.js
quando il filepackage.json
principale più vicino contiene un campo di primo livello"type"
con il valore"commonjs"
. - File con estensione
.js
o senza estensione, quando il filepackage.json
principale più vicino non contiene un campo di primo livello"type"
oppure non esiste unpackage.json
in alcuna cartella principale; a meno che il file non contenga una sintassi che genera errori a meno che non venga valutato come modulo ES. Gli autori dei pacchetti dovrebbero includere il campo"type"
, anche nei pacchetti in cui tutte le fonti sono CommonJS. Essere espliciti sultipo
del pacchetto renderà più semplice per gli strumenti di compilazione e i loader determinare come i file nel pacchetto dovrebbero essere interpretati. - File con un'estensione che non è
.mjs
,.cjs
,.json
,.node
o.js
(quando il filepackage.json
principale più vicino contiene un campo di primo livello"type"
con valore"module"
, tali file verranno riconosciuti come moduli CommonJS solo se vengono inclusi tramiterequire()
, non quando vengono utilizzati come punto di ingresso della riga di comando del programma).
Vedi Determinazione del sistema di moduli per maggiori dettagli.
La chiamata a require()
utilizza sempre il caricatore di moduli CommonJS. La chiamata a import()
utilizza sempre il caricatore di moduli ECMAScript.
Accesso al modulo principale
Quando un file viene eseguito direttamente da Node.js, require.main
viene impostato sul suo module
. Ciò significa che è possibile determinare se un file è stato eseguito direttamente testando require.main === module
.
Per un file foo.js
, questo sarà true
se eseguito tramite node foo.js
, ma false
se eseguito da require('./foo')
.
Quando il punto di ingresso non è un modulo CommonJS, require.main
è undefined
e il modulo principale non è raggiungibile.
Suggerimenti per i gestori di pacchetti
La semantica della funzione require()
di Node.js è stata progettata per essere sufficientemente generale da supportare strutture di directory ragionevoli. Programmi di gestione dei pacchetti come dpkg
, rpm
e npm
sperano di poter costruire pacchetti nativi dai moduli Node.js senza modifiche.
Di seguito, forniamo una struttura di directory suggerita che potrebbe funzionare:
Supponiamo di voler che la cartella in /usr/lib/node/\<some-package\>/\<some-version\>
contenga il contenuto di una specifica versione di un pacchetto.
I pacchetti possono dipendere l'uno dall'altro. Per installare il pacchetto foo
, potrebbe essere necessario installare una specifica versione del pacchetto bar
. Il pacchetto bar
stesso potrebbe avere dipendenze e, in alcuni casi, queste potrebbero persino entrare in conflitto o formare dipendenze cicliche.
Poiché Node.js cerca il realpath
di qualsiasi modulo che carica (ovvero, risolve i collegamenti simbolici) e quindi cerca le loro dipendenze nelle cartelle node_modules
, questa situazione può essere risolta con la seguente architettura:
/usr/lib/node/foo/1.2.3/
: Contenuto del pacchettofoo
, versione 1.2.3./usr/lib/node/bar/4.3.2/
: Contenuto del pacchettobar
da cui dipendefoo
./usr/lib/node/foo/1.2.3/node_modules/bar
: Collegamento simbolico a/usr/lib/node/bar/4.3.2/
./usr/lib/node/bar/4.3.2/node_modules/*
: Collegamenti simbolici ai pacchetti da cui dipendebar
.
Pertanto, anche se si verifica un ciclo o se ci sono conflitti di dipendenza, ogni modulo sarà in grado di ottenere una versione della sua dipendenza che può utilizzare.
Quando il codice nel pacchetto foo
esegue require('bar')
, otterrà la versione collegata simbolicamente in /usr/lib/node/foo/1.2.3/node_modules/bar
. Quindi, quando il codice nel pacchetto bar
chiama require('quux')
, otterrà la versione collegata simbolicamente in /usr/lib/node/bar/4.3.2/node_modules/quux
.
Inoltre, per rendere il processo di ricerca dei moduli ancora più ottimale, anziché inserire i pacchetti direttamente in /usr/lib/node
, potremmo inserirli in /usr/lib/node_modules/\<nome\>/\<versione\>
. Quindi Node.js non si preoccuperà di cercare dipendenze mancanti in /usr/node_modules
o /node_modules
.
Per rendere i moduli disponibili per la REPL di Node.js, potrebbe essere utile aggiungere anche la cartella /usr/lib/node_modules
alla variabile d'ambiente $NODE_PATH
. Poiché le ricerche di moduli che utilizzano le cartelle node_modules
sono tutte relative e basate sul percorso reale dei file che effettuano le chiamate a require()
, i pacchetti stessi possono trovarsi ovunque.
Caricamento di moduli ECMAScript utilizzando require()
[Cronologia]
Versione | Modifiche |
---|---|
v23.5.0 | Questa funzionalità non emette più un avviso sperimentale di default, anche se l'avviso può ancora essere emesso da --trace-require-module. |
v23.0.0 | Questa funzionalità non è più dietro il flag CLI --experimental-require-module . |
v23.0.0 | Supporto all'interoperabilità dell'esportazione 'module.exports' in require(esm) . |
v22.0.0, v20.17.0 | Aggiunto in: v22.0.0, v20.17.0 |
[Stabile: 1 - Sperimentale]
Stabile: 1 Stabilità: 1.2 - Release candidate
L'estensione .mjs
è riservata per i Moduli ECMAScript. Consulta la sezione Determinazione del sistema di moduli per ulteriori informazioni su quali file vengono analizzati come moduli ECMAScript.
require()
supporta solo il caricamento di moduli ECMAScript che soddisfano i seguenti requisiti:
- Il modulo è completamente sincrono (non contiene
await
di livello superiore); e - Una di queste condizioni è soddisfatta:
Se il modulo ES che viene caricato soddisfa i requisiti, require()
può caricarlo e restituire l'oggetto dello spazio dei nomi del modulo. In questo caso è simile a import()
dinamico, ma viene eseguito in modo sincrono e restituisce direttamente l'oggetto dello spazio dei nomi.
Con i seguenti moduli ES:
// distanza.mjs
export function distance(a, b) {
return (b.x - a.x) ** 2 + (b.y - a.y) ** 2
}
// punto.mjs
export default class Point {
constructor(x, y) {
this.x = x
this.y = y
}
}
Un modulo CommonJS può caricarli con require()
:
const distance = require('./distance.mjs')
console.log(distance)
// [Module: null prototype] {
// distance: [Function: distanza]
// }
const point = require('./point.mjs')
console.log(point)
// [Module: null prototype] {
// default: [class Point],
// __esModule: true,
// }
Per l'interoperabilità con gli strumenti esistenti che convertono i Moduli ES in CommonJS, che potrebbero quindi caricare i veri Moduli ES tramite require()
, lo spazio dei nomi restituito conterrebbe una proprietà __esModule: true
se ha un'esportazione default
in modo che il codice di utilizzo generato dagli strumenti possa riconoscere le esportazioni predefinite nei veri Moduli ES. Se lo spazio dei nomi definisce già __esModule
, questo non verrebbe aggiunto. Questa proprietà è sperimentale e può cambiare in futuro. Dovrebbe essere utilizzata solo da strumenti che convertono moduli ES in moduli CommonJS, seguendo le convenzioni dell'ecosistema esistente. Il codice scritto direttamente in CommonJS dovrebbe evitare di dipendere da esso.
Quando un modulo ES contiene sia esportazioni denominate che un'esportazione predefinita, il risultato restituito da require()
è l'oggetto dello spazio dei nomi del modulo, che inserisce l'esportazione predefinita nella proprietà .default
, in modo simile ai risultati restituiti da import()
. Per personalizzare ciò che dovrebbe essere restituito direttamente da require(esm)
, il modulo ES può esportare il valore desiderato utilizzando il nome stringa "module.exports"
.
// punto.mjs
export default class Point {
constructor(x, y) {
this.x = x
this.y = y
}
}
// `distanza` è persa per i consumatori CommonJS di questo modulo, a meno che non sia
// aggiunta a `Point` come proprietà statica.
export function distance(a, b) {
return (b.x - a.x) ** 2 + (b.y - a.y) ** 2
}
export { Point as 'module.exports' }
const Point = require('./point.mjs')
console.log(Point) // [class Point]
// Le esportazioni denominate vengono perse quando viene utilizzato 'module.exports'
const { distance } = require('./point.mjs')
console.log(distance) // undefined
Si noti nell'esempio precedente che quando viene utilizzato il nome di esportazione module.exports
, le esportazioni denominate andranno perse per i consumatori CommonJS. Per consentire ai consumatori CommonJS di continuare ad accedere alle esportazioni denominate, il modulo può assicurarsi che l'esportazione predefinita sia un oggetto con le esportazioni denominate associate ad esso come proprietà. Ad esempio, con l'esempio precedente, distance
può essere associata all'esportazione predefinita, la classe Point
, come metodo statico.
export function distance(a, b) {
return (b.x - a.x) ** 2 + (b.y - a.y) ** 2
}
export default class Point {
constructor(x, y) {
this.x = x
this.y = y
}
static distance = distance
}
export { Point as 'module.exports' }
const Point = require('./point.mjs')
console.log(Point) // [class Point]
const { distance } = require('./point.mjs')
console.log(distance) // [Function: distanza]
Se il modulo che viene require()
'd contiene await
di livello superiore o il grafico dei moduli che import
contiene await
di livello superiore, verrà lanciato ERR_REQUIRE_ASYNC_MODULE
. In questo caso, gli utenti devono caricare il modulo asincrono utilizzando import()
.
Se --experimental-print-required-tla
è abilitato, invece di lanciare ERR_REQUIRE_ASYNC_MODULE
prima della valutazione, Node.js valuterà il modulo, cercherà di individuare gli await di livello superiore e stamperà la loro posizione per aiutare gli utenti a correggerli.
Il supporto per il caricamento di moduli ES utilizzando require()
è attualmente sperimentale e può essere disabilitato utilizzando --no-experimental-require-module
. Per stampare dove viene utilizzata questa funzionalità, utilizzare --trace-require-module
.
Questa funzionalità può essere rilevata verificando se process.features.require_module
è true
.
Tutto insieme
Per ottenere il nome del file esatto che verrà caricato quando viene chiamato require()
, utilizza la funzione require.resolve()
.
Mettendo insieme tutto quanto sopra, ecco l'algoritmo di alto livello in pseudocodice di ciò che fa require()
:
require(X) da modulo nel percorso Y
1. Se X è un modulo core,
a. restituisci il modulo core
b. STOP
2. Se X inizia con '/'
a. imposta Y come radice del file system
3. Se X inizia con './' o '/' o '../'
a. CARICA_COME_FILE(Y + X)
b. CARICA_COME_DIRECTORY(Y + X)
c. LANCIA "non trovato"
4. Se X inizia con '#'
a. CARICA_IMPORTAZIONI_PACCHETTO(X, dirname(Y))
5. CARICA_PACCHETTO_SELF(X, dirname(Y))
6. CARICA_NODE_MODULES(X, dirname(Y))
7. LANCIA "non trovato"
FORSE_RILEVA_E_CARICA(X)
1. Se X viene analizzato come modulo CommonJS, carica X come modulo CommonJS. STOP.
2. Altrimenti, se il codice sorgente di X può essere analizzato come modulo ECMAScript utilizzando
<a href="esm#resolver-algorithm-specification">RILEVA_SINTASSI_MODULO definito nel
risolutore ESM</a>,
a. Carica X come modulo ECMAScript. STOP.
3. LANCIA SyntaxError dal tentativo di analizzare X come CommonJS in 1. STOP.
CARICA_COME_FILE(X)
1. Se X è un file, carica X nel formato della sua estensione di file. STOP
2. Se X.js è un file,
a. Trova l'ambito del pacchetto SCOPE più vicino a X.
b. Se non è stato trovato alcun ambito
1. FORSE_RILEVA_E_CARICA(X.js)
c. Se SCOPE/package.json contiene il campo "type",
1. Se il campo "type" è "module", carica X.js come modulo ECMAScript. STOP.
2. Se il campo "type" è "commonjs", carica X.js come modulo CommonJS. STOP.
d. FORSE_RILEVA_E_CARICA(X.js)
3. Se X.json è un file, carica X.json in un oggetto JavaScript. STOP
4. Se X.node è un file, carica X.node come addon binario. STOP
CARICA_INDEX(X)
1. Se X/index.js è un file
a. Trova l'ambito del pacchetto SCOPE più vicino a X.
b. Se non è stato trovato alcun ambito, carica X/index.js come modulo CommonJS. STOP.
c. Se SCOPE/package.json contiene il campo "type",
1. Se il campo "type" è "module", carica X/index.js come modulo ECMAScript. STOP.
2. Altrimenti, carica X/index.js come modulo CommonJS. STOP.
2. Se X/index.json è un file, analizza X/index.json in un oggetto JavaScript. STOP
3. Se X/index.node è un file, carica X/index.node come addon binario. STOP
CARICA_COME_DIRECTORY(X)
1. Se X/package.json è un file,
a. Analizza X/package.json e cerca il campo "main".
b. Se "main" è un valore falsy, VAI AL PUNTO 2.
c. sia M = X + (campo main json)
d. CARICA_COME_FILE(M)
e. CARICA_INDEX(M)
f. CARICA_INDEX(X) DEPRECATO
g. LANCIA "non trovato"
2. CARICA_INDEX(X)
CARICA_NODE_MODULES(X, START)
1. siano DIRS = PERCORSI_NODE_MODULES(START)
2. per ogni DIR in DIRS:
a. CARICA_ESPORTAZIONI_PACCHETTO(X, DIR)
b. CARICA_COME_FILE(DIR/X)
c. CARICA_COME_DIRECTORY(DIR/X)
PERCORSI_NODE_MODULES(START)
1. siano PARTI = divisione percorso(START)
2. sia I = conteggio di PARTI - 1
3. siano DIRS = []
4. finché I >= 0,
a. se PARTI[I] = "node_modules", VAI A d.
b. DIR = unione percorso(PARTI[0 .. I] + "node_modules")
c. DIRS = DIR + DIRS
d. sia I = I - 1
5. restituisci DIRS + CARTELLE_GLOBALI
CARICA_IMPORTAZIONI_PACCHETTO(X, DIR)
1. Trova l'ambito del pacchetto SCOPE più vicino a DIR.
2. Se non è stato trovato alcun ambito, restituisci.
3. Se il campo "imports" di SCOPE/package.json è null o undefined, restituisci.
4. Se `--experimental-require-module` è abilitato
a. siano CONDIZIONI = ["node", "require", "module-sync"]
b. Altrimenti, siano CONDIZIONI = ["node", "require"]
5. sia MATCH = RISOLVI_IMPORTAZIONI_PACCHETTO(X, pathToFileURL(SCOPE),
CONDIZIONI) <a href="esm#resolver-algorithm-specification">definito nel risolutore ESM</a>.
6. RISOLVI_CORRISPONDENZA_ESM(MATCH).
CARICA_ESPORTAZIONI_PACCHETTO(X, DIR)
1. Prova a interpretare X come combinazione di NOME e SOTTOPERCORSO dove il nome
può avere un prefisso @ambito/ e il sottopercorso inizia con una barra (`/`).
2. Se X non corrisponde a questo modello o DIR/NOME/package.json non è un file,
restituisci.
3. Analizza DIR/NOME/package.json e cerca il campo "exports".
4. Se "exports" è null o undefined, restituisci.
5. Se `--experimental-require-module` è abilitato
a. siano CONDIZIONI = ["node", "require", "module-sync"]
b. Altrimenti, siano CONDIZIONI = ["node", "require"]
6. sia MATCH = RISOLVI_ESPORTAZIONI_PACCHETTO(pathToFileURL(DIR/NOME), "." + SOTTOPERCORSO,
`package.json` "exports", CONDIZIONI) <a href="esm#resolver-algorithm-specification">definito nel risolutore ESM</a>.
7. RISOLVI_CORRISPONDENZA_ESM(MATCH)
CARICA_PACCHETTO_SELF(X, DIR)
1. Trova l'ambito del pacchetto SCOPE più vicino a DIR.
2. Se non è stato trovato alcun ambito, restituisci.
3. Se il campo "exports" di SCOPE/package.json è null o undefined, restituisci.
4. Se il campo "name" di SCOPE/package.json non è il primo segmento di X, restituisci.
5. sia MATCH = RISOLVI_ESPORTAZIONI_PACCHETTO(pathToFileURL(SCOPE),
"." + X.slice("name".length), `package.json` "exports", ["node", "require"])
<a href="esm#resolver-algorithm-specification">definito nel risolutore ESM</a>.
6. RISOLVI_CORRISPONDENZA_ESM(MATCH)
RISOLVI_CORRISPONDENZA_ESM(MATCH)
1. sia PERCORSO_RISOLTO = fileURLToPath(MATCH)
2. Se il file in PERCORSO_RISOLTO esiste, carica PERCORSO_RISOLTO nel formato della sua estensione.
STOP
3. LANCIA "non trovato"
Memorizzazione nella Cache
I moduli vengono memorizzati nella cache dopo il primo caricamento. Ciò significa (tra le altre cose) che ogni chiamata a require('foo')
otterrà esattamente lo stesso oggetto restituito, se si risolvesse nello stesso file.
A condizione che require.cache
non venga modificato, più chiamate a require('foo')
non faranno sì che il codice del modulo venga eseguito più volte. Questa è una caratteristica importante. Con essa, è possibile restituire oggetti "parzialmente completati", consentendo così di caricare dipendenze transitive anche quando causerebbero cicli.
Per far eseguire il codice di un modulo più volte, esporta una funzione e chiama tale funzione.
Avvertenze sulla memorizzazione nella cache dei moduli
I moduli vengono memorizzati nella cache in base al nome del file risolto. Poiché i moduli possono essere risolti in un nome file diverso in base alla posizione del modulo chiamante (caricamento dalle cartelle node_modules
), non è una garanzia che require('foo')
restituirà sempre esattamente lo stesso oggetto, se si risolvesse in file diversi.
Inoltre, su file system o sistemi operativi non sensibili alle maiuscole e minuscole, nomi di file risolti diversi possono puntare allo stesso file, ma la cache li tratterà comunque come moduli diversi e ricaricherà il file più volte. Ad esempio, require('./foo')
e require('./FOO')
restituiscono due oggetti diversi, indipendentemente dal fatto che ./foo
e ./FOO
siano lo stesso file o meno.
Moduli integrati
[Cronologia]
Versione | Modifiche |
---|---|
v16.0.0, v14.18.0 | Aggiunto il supporto all'importazione node: a require(...) . |
Node.js ha diversi moduli compilati nel binario. Questi moduli sono descritti in dettaglio altrove in questa documentazione.
I moduli integrati sono definiti all'interno del sorgente di Node.js e si trovano nella cartella lib/
.
I moduli integrati possono essere identificati utilizzando il prefisso node:
, nel qual caso bypassa la cache require
. Ad esempio, require('node:http')
restituirà sempre il modulo HTTP integrato, anche se esiste una voce require.cache
con quel nome.
Alcuni moduli integrati vengono sempre caricati preferenzialmente se il loro identificatore viene passato a require()
. Ad esempio, require('http')
restituirà sempre il modulo HTTP integrato, anche se esiste un file con quel nome. L'elenco dei moduli integrati che possono essere caricati senza utilizzare il prefisso node:
è esposto in module.builtinModules
, elencati senza il prefisso.
Moduli integrati con prefisso node:
obbligatorio
Quando vengono caricati tramite require()
, alcuni moduli integrati devono essere richiesti con il prefisso node:
. Questo requisito esiste per evitare che i moduli integrati di nuova introduzione entrino in conflitto con i pacchetti utente che hanno già assunto il nome. Attualmente, i moduli integrati che richiedono il prefisso node:
sono:
L'elenco di questi moduli è esposto in module.builtinModules
, incluso il prefisso.
Cicli
Quando ci sono chiamate require()
circolari, un modulo potrebbe non aver terminato l'esecuzione quando viene restituito.
Considera questa situazione:
a.js
:
console.log('a starting')
exports.done = false
const b = require('./b.js')
console.log('in a, b.done = %j', b.done)
exports.done = true
console.log('a done')
b.js
:
console.log('b starting')
exports.done = false
const a = require('./a.js')
console.log('in b, a.done = %j', a.done)
exports.done = true
console.log('b done')
main.js
:
console.log('main starting')
const a = require('./a.js')
const b = require('./b.js')
console.log('in main, a.done = %j, b.done = %j', a.done, b.done)
Quando main.js
carica a.js
, quindi a.js
a sua volta carica b.js
. A quel punto, b.js
prova a caricare a.js
. Per evitare un loop infinito, una copia non finita dell'oggetto exports di a.js
viene restituita al modulo b.js
. b.js
termina quindi il caricamento e il suo oggetto exports
viene fornito al modulo a.js
.
Quando main.js
ha caricato entrambi i moduli, entrambi sono terminati. L'output di questo programma sarebbe quindi:
$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done = true, b.done = true
È necessaria un'attenta pianificazione per consentire alle dipendenze cicliche dei moduli di funzionare correttamente all'interno di un'applicazione.
Moduli file
Se non viene trovato l'esatto nome del file, Node.js tenterà di caricare il nome del file richiesto con le estensioni aggiunte: .js
, .json
e infine .node
. Quando si carica un file con un'estensione diversa (ad es. .cjs
), il suo nome completo deve essere passato a require()
, inclusa la sua estensione (ad es. require('./file.cjs')
).
I file .json
vengono analizzati come file di testo JSON, i file .node
vengono interpretati come moduli aggiuntivi compilati caricati con process.dlopen()
. I file che utilizzano qualsiasi altra estensione (o nessuna estensione) vengono analizzati come file di testo JavaScript. Fare riferimento alla sezione Determinazione del sistema di moduli per capire quale obiettivo di analisi verrà utilizzato.
Un modulo richiesto con il prefisso '/'
è un percorso assoluto per il file. Ad esempio, require('/home/marco/foo.js')
caricherà il file in /home/marco/foo.js
.
Un modulo richiesto con il prefisso './'
è relativo al file che chiama require()
. Cioè, circle.js
deve trovarsi nella stessa directory di foo.js
affinché require('./circle')
lo trovi.
Senza un prefisso iniziale '/'
, './'
o '../'
per indicare un file, il modulo deve essere un modulo core o viene caricato da una cartella node_modules
.
Se il percorso specificato non esiste, require()
genererà un errore MODULE_NOT_FOUND
.
Cartelle come moduli
[Stabile: 3 - Legacy]
Stabile: 3 Stabilità: 3 - Legacy: Utilizzare esportazioni di sottopercorsi o importazioni di sottopercorsi.
Ci sono tre modi in cui una cartella può essere passata a require()
come argomento.
Il primo è creare un file package.json
nella root della cartella, che specifica un modulo main
. Un esempio di file package.json
potrebbe essere simile a questo:
{ "name": "some-library", "main": "./lib/some-library.js" }
Se questo si trovasse in una cartella in ./some-library
, allora require('./some-library')
tenterebbe di caricare ./some-library/lib/some-library.js
.
Se non è presente alcun file package.json
nella directory, o se la voce "main"
manca o non può essere risolta, Node.js tenterà di caricare un file index.js
o index.node
da quella directory. Ad esempio, se non ci fosse un file package.json
nell'esempio precedente, allora require('./some-library')
tenterebbe di caricare:
./some-library/index.js
./some-library/index.node
Se questi tentativi falliscono, Node.js segnalerà l'intero modulo come mancante con l'errore predefinito:
Error: Cannot find module 'some-library'
In tutti e tre i casi sopra, una chiamata import('./some-library')
risulterebbe in un errore ERR_UNSUPPORTED_DIR_IMPORT
. L'utilizzo di esportazioni di sottopercorsi o importazioni di sottopercorsi del pacchetto può fornire gli stessi vantaggi di organizzazione del contenimento delle cartelle come moduli e funzionare sia per require
che per import
.
Caricamento dalle cartelle node_modules
Se l'identificatore del modulo passato a require()
non è un modulo integrato e non inizia con '/'
, '../'
o './'
, allora Node.js inizia dalla directory del modulo corrente, aggiunge /node_modules
e tenta di caricare il modulo da quella posizione. Node.js non aggiungerà node_modules
a un percorso che termina già con node_modules
.
Se non viene trovato lì, si sposta nella directory principale e così via, fino a raggiungere la radice del file system.
Ad esempio, se il file in '/home/ry/projects/foo.js'
chiamasse require('bar.js')
, allora Node.js cercherebbe nelle seguenti posizioni, in questo ordine:
/home/ry/projects/node_modules/bar.js
/home/ry/node_modules/bar.js
/home/node_modules/bar.js
/node_modules/bar.js
Questo consente ai programmi di localizzare le proprie dipendenze, in modo che non entrino in conflitto.
È possibile richiedere file specifici o sottomoduli distribuiti con un modulo includendo un suffisso di percorso dopo il nome del modulo. Ad esempio, require('example-module/path/to/file')
risolverebbe path/to/file
rispetto alla posizione di example-module
. Il percorso con suffisso segue la stessa semantica di risoluzione dei moduli.
Caricamento dalle cartelle globali
Se la variabile d'ambiente NODE_PATH
è impostata su un elenco di percorsi assoluti delimitati da due punti, allora Node.js cercherà i moduli in quei percorsi se non vengono trovati altrove.
Su Windows, NODE_PATH
è delimitato da punti e virgola (;
) anziché da due punti.
NODE_PATH
è stato originariamente creato per supportare il caricamento di moduli da percorsi variabili prima che fosse definito l'attuale algoritmo di risoluzione dei moduli.
NODE_PATH
è ancora supportato, ma è meno necessario ora che l'ecosistema Node.js si è stabilito su una convenzione per localizzare i moduli dipendenti. A volte le distribuzioni che si basano su NODE_PATH
mostrano un comportamento sorprendente quando le persone non sono consapevoli che NODE_PATH
deve essere impostato. A volte le dipendenze di un modulo cambiano, causando il caricamento di una versione diversa (o anche di un modulo diverso) durante la ricerca in NODE_PATH
.
Inoltre, Node.js cercherà nel seguente elenco di GLOBAL_FOLDERS:
- 1:
$HOME/.node_modules
- 2:
$HOME/.node_libraries
- 3:
$PREFIX/lib/node
Dove $HOME
è la directory home dell'utente e $PREFIX
è il node_prefix
configurato di Node.js.
Questi sono principalmente per motivi storici.
Si consiglia vivamente di inserire le dipendenze nella cartella node_modules
locale. Queste verranno caricate più velocemente e in modo più affidabile.
Il wrapper del modulo
Prima che il codice di un modulo venga eseguito, Node.js lo avvolge con un wrapper di funzione simile al seguente:
;(function (exports, require, module, __filename, __dirname) {
// Il codice del modulo vive effettivamente qui
})
Facendo ciò, Node.js ottiene alcuni risultati:
- Mantiene le variabili di livello superiore (definite con
var
,const
olet
) con ambito al modulo piuttosto che all'oggetto globale. - Aiuta a fornire alcune variabili dall'aspetto globale che sono in realtà specifiche del modulo, come:
- Gli oggetti
module
eexports
che l'implementatore può utilizzare per esportare valori dal modulo. - Le variabili di convenienza
__filename
e__dirname
, contenenti il nome file assoluto e il percorso della directory del modulo.
- Gli oggetti
L'ambito del modulo
__dirname
Aggiunto in: v0.1.27
Il nome della directory del modulo corrente. Questo è lo stesso di path.dirname()
di __filename
.
Esempio: esecuzione di node example.js
da /Users/mjr
console.log(__dirname)
// Stampa: /Users/mjr
console.log(path.dirname(__filename))
// Stampa: /Users/mjr
__filename
Aggiunto in: v0.0.1
Il nome del file del modulo corrente. Questo è il percorso assoluto del file del modulo corrente con i symlink risolti.
Per un programma principale, questo non è necessariamente lo stesso del nome file utilizzato nella riga di comando.
Vedi __dirname
per il nome della directory del modulo corrente.
Esempi:
Esecuzione di node example.js
da /Users/mjr
console.log(__filename)
// Stampa: /Users/mjr/example.js
console.log(__dirname)
// Stampa: /Users/mjr
Dati due moduli: a
e b
, dove b
è una dipendenza di a
e c'è una struttura di directory di:
/Users/mjr/app/a.js
/Users/mjr/app/node_modules/b/b.js
I riferimenti a __filename
all'interno di b.js
restituiranno /Users/mjr/app/node_modules/b/b.js
mentre i riferimenti a __filename
all'interno di a.js
restituiranno /Users/mjr/app/a.js
.
exports
Aggiunto in: v0.1.12
Un riferimento a module.exports
che è più breve da scrivere. Consulta la sezione relativa al collegamento exports per i dettagli su quando usare exports
e quando usare module.exports
.
module
Aggiunto in: v0.1.16
Un riferimento al modulo corrente, consulta la sezione relativa all'oggetto module
. In particolare, module.exports
viene utilizzato per definire cosa esporta un modulo e rende disponibile tramite require()
.
require(id)
Aggiunto in: v0.1.13
Usato per importare moduli, JSON
e file locali. I moduli possono essere importati da node_modules
. I moduli locali e i file JSON possono essere importati usando un percorso relativo (es. ./
, ./foo
, ./bar/baz
, ../foo
) che verrà risolto rispetto alla directory indicata da __dirname
(se definito) o la directory di lavoro corrente. I percorsi relativi in stile POSIX vengono risolti in modo indipendente dal sistema operativo, il che significa che gli esempi sopra funzioneranno su Windows nello stesso modo in cui funzionerebbero sui sistemi Unix.
// Importazione di un modulo locale con un percorso relativo a `__dirname` o alla directory di lavoro corrente.
// (Su Windows, questo si risolverebbe in .\path\myLocalModule.)
const myLocalModule = require('./path/myLocalModule')
// Importazione di un file JSON:
const jsonData = require('./path/filename.json')
// Importazione di un modulo da node_modules o un modulo integrato di Node.js:
const crypto = require('node:crypto')
require.cache
Aggiunto in: v0.3.0
I moduli vengono memorizzati nella cache in questo oggetto quando vengono richiesti. Eliminando un valore chiave da questo oggetto, il successivo require
ricaricherà il modulo. Ciò non si applica agli addon nativi, per i quali la ricarica provocherà un errore.
È anche possibile aggiungere o sostituire le voci. Questa cache viene controllata prima dei moduli integrati e se un nome corrispondente a un modulo integrato viene aggiunto alla cache, solo le chiamate di require con prefisso node:
riceveranno il modulo integrato. Usare con attenzione!
const assert = require('node:assert')
const realFs = require('node:fs')
const fakeFs = {}
require.cache.fs = { exports: fakeFs }
assert.strictEqual(require('fs'), fakeFs)
assert.strictEqual(require('node:fs'), realFs)
require.extensions
Aggiunto in: v0.3.0
Deprecato a partire da: v0.10.6
[Stabile: 0 - Deprecato]
Stabile: 0 Stabilità: 0 - Deprecato
Istruisce require
su come gestire determinate estensioni di file.
Elabora i file con l'estensione .sjs
come .js
:
require.extensions['.sjs'] = require.extensions['.js']
Deprecato. In passato, questo elenco veniva utilizzato per caricare moduli non JavaScript in Node.js compilando su richiesta. Tuttavia, in pratica, ci sono modi molto migliori per farlo, come caricare moduli tramite un altro programma Node.js o compilarli in JavaScript in anticipo.
Evita di utilizzare require.extensions
. L'uso potrebbe causare bug sottili e la risoluzione delle estensioni diventa più lenta con ogni estensione registrata.
require.main
Aggiunto in: v0.1.17
L'oggetto Module
che rappresenta lo script di ingresso caricato quando è stato avviato il processo Node.js, oppure undefined
se il punto di ingresso del programma non è un modulo CommonJS. Vedi "Accesso al modulo principale".
Nello script entry.js
:
console.log(require.main)
node entry.js
Module {
id: '.',
path: '/absolute/path/to',
exports: {},
filename: '/absolute/path/to/entry.js',
loaded: false,
children: [],
paths:
[ '/absolute/path/to/node_modules',
'/absolute/path/node_modules',
'/absolute/node_modules',
'/node_modules' ] }
require.resolve(request[, options])
[Cronologia]
Versione | Modifiche |
---|---|
v8.9.0 | L'opzione paths ora è supportata. |
v0.3.0 | Aggiunto in: v0.3.0 |
request
<string> Il percorso del modulo da risolvere.options
<Object>paths
<string[]> Percorsi da cui risolvere la posizione del modulo. Se presente, questi percorsi vengono utilizzati invece dei percorsi di risoluzione predefiniti, ad eccezione di GLOBAL_FOLDERS come$HOME/.node_modules
, che sono sempre inclusi. Ciascuno di questi percorsi viene utilizzato come punto di partenza per l'algoritmo di risoluzione del modulo, il che significa che la gerarchianode_modules
viene controllata da questa posizione.
Restituisce: <string>
Utilizza il meccanismo interno require()
per cercare la posizione di un modulo, ma invece di caricare il modulo, restituisce semplicemente il nome del file risolto.
Se il modulo non viene trovato, viene generato un errore MODULE_NOT_FOUND
.
require.resolve.paths(request)
Aggiunto in: v8.9.0
request
<string> Il percorso del modulo di cui vengono recuperati i percorsi di ricerca.- Restituisce: <string[]> | <null>
Restituisce un array contenente i percorsi cercati durante la risoluzione di request
o null
se la stringa request
fa riferimento a un modulo core, ad esempio http
o fs
.
L'oggetto module
Aggiunto in: v0.1.16
In ogni modulo, la variabile libera module
è un riferimento all'oggetto che rappresenta il modulo corrente. Per comodità, module.exports
è accessibile anche tramite il modulo globale exports
. module
non è in realtà una variabile globale, ma piuttosto locale a ogni modulo.
module.children
Aggiunto in: v0.1.16
Gli oggetti modulo richiesti per la prima volta da questo.
module.exports
Aggiunto in: v0.1.16
L'oggetto module.exports
viene creato dal sistema Module
. A volte questo non è accettabile; molti vogliono che il loro modulo sia un'istanza di una certa classe. Per fare ciò, assegna l'oggetto di esportazione desiderato a module.exports
. L'assegnazione dell'oggetto desiderato a exports
si limiterà a riassociare la variabile locale exports
, cosa che probabilmente non è ciò che si desidera.
Ad esempio, supponiamo di creare un modulo chiamato a.js
:
const EventEmitter = require('node:events')
module.exports = new EventEmitter()
// Esegui del lavoro e dopo un po' emetti
// l'evento 'ready' dal modulo stesso.
setTimeout(() => {
module.exports.emit('ready')
}, 1000)
Quindi in un altro file potremmo fare:
const a = require('./a')
a.on('ready', () => {
console.log('il modulo "a" è pronto')
})
L'assegnazione a module.exports
deve essere eseguita immediatamente. Non può essere fatta in nessun callback. Questo non funziona:
x.js
:
setTimeout(() => {
module.exports = { a: 'hello' }
}, 0)
y.js
:
const x = require('./x')
console.log(x.a)
Scorciatoia exports
Aggiunto in: v0.1.16
La variabile exports
è disponibile all'interno dello scope a livello di file di un modulo, e le viene assegnato il valore di module.exports
prima che il modulo venga valutato.
Consente una scorciatoia, in modo che module.exports.f = ...
possa essere scritto in modo più succinto come exports.f = ...
. Tuttavia, è necessario essere consapevoli che, come qualsiasi variabile, se viene assegnato un nuovo valore a exports
, non è più collegato a module.exports
:
module.exports.hello = true // Esportato dal require del modulo
exports = { hello: false } // Non esportato, disponibile solo nel modulo
Quando la proprietà module.exports
viene completamente sostituita da un nuovo oggetto, è comune riassegnare anche exports
:
module.exports = exports = function Constructor() {
// ... ecc.
}
Per illustrare il comportamento, immagina questa ipotetica implementazione di require()
, che è molto simile a ciò che viene effettivamente fatto da require()
:
function require(/* ... */) {
const module = { exports: {} }
;((module, exports) => {
// Codice del modulo qui. In questo esempio, definire una funzione.
function someFunc() {}
exports = someFunc
// A questo punto, exports non è più una scorciatoia per module.exports, e
// questo modulo continuerà a esportare un oggetto predefinito vuoto.
module.exports = someFunc
// A questo punto, il modulo esporterà someFunc, invece dell'oggetto
// predefinito.
})(module, module.exports)
return module.exports
}
module.filename
Aggiunto in: v0.1.16
Il nome file completo e risolto del modulo.
module.id
Aggiunto in: v0.1.16
L'identificatore del modulo. In genere si tratta del nome file completo risolto.
module.isPreloading
Aggiunto in: v15.4.0, v14.17.0
- Tipo: <booleano>
true
se il modulo è in esecuzione durante la fase di precaricamento di Node.js.
module.loaded
Aggiunto in: v0.1.16
Indica se il caricamento del modulo è completato o è in fase di caricamento.
module.parent
Aggiunto in: v0.1.16
Deprecato a partire da: v14.6.0, v12.19.0
[Stabile: 0 - Deprecato]
Stabile: 0 Stabilità: 0 - Deprecato: Si prega di utilizzare require.main
e module.children
invece.
Il modulo che per primo ha richiesto questo, o null
se il modulo corrente è il punto di ingresso del processo corrente, o undefined
se il modulo è stato caricato da qualcosa che non è un modulo CommonJS (es: REPL o import
).
module.path
Aggiunto in: v11.14.0
Il nome della directory del modulo. Di solito è lo stesso di path.dirname()
di module.id
.
module.paths
Aggiunto in: v0.4.0
I percorsi di ricerca per il modulo.
module.require(id)
Aggiunto in: v0.5.1
Il metodo module.require()
fornisce un modo per caricare un modulo come se require()
fosse stato chiamato dal modulo originale.
Per fare ciò, è necessario ottenere un riferimento all'oggetto module
. Poiché require()
restituisce module.exports
e module
è in genere disponibile solo all'interno del codice di un modulo specifico, deve essere esportato esplicitamente per essere utilizzato.
L'oggetto Module
Questa sezione è stata spostata a Moduli: modulo principale module
.
Supporto per Source map v3
Questa sezione è stata spostata a Moduli: modulo principale module
.