path.join() unisce i segmenti di un percorso con il separatore giusto e sistema i .. e le barre doppie, ma lascia il risultato relativo se sei partito da un segmento relativo. path.resolve() fa lo stesso lavoro da destra a sinistra e restituisce sempre un percorso assoluto: parte dalla directory di lavoro corrente, e ogni segmento assoluto che incontra fa ripartire il percorso. Usa join per costruire un percorso e resolve quando te ne serve uno assoluto.
Ha stampato users/ada/notes.txt, poi users/grace/todo.txt (il .. ha tolto ada), poi /srv/app/public/index.html e src/lib/utils.js: le barre in più e i segmenti vuoti sono spariti. join non tratta mai /public/ come un nuovo inizio; è solo il segmento successivo.
path.join e path.resolve a confronto
La differenza si vede quando un segmento inizia con / o quando il primo segmento è relativo:
Con /public nel mezzo, join ha prodotto /srv/app/public/img mentre resolve ha buttato via tutto quello che c'era prima e ha prodotto /public/img; entrambi hanno gestito .. allo stesso modo. Le ultime due righe mostrano che join ha lasciato relativo public/index.html, mentre resolve l'ha reso assoluto mettendo davanti la cartella corrente.
path.join | path.resolve | |
|---|---|---|
| Risultato | Relativo o assoluto, come l'input | Sempre assoluto |
Un segmento che inizia con / | Un segmento come gli altri | Fa ripartire il percorso da lì |
| Punto di partenza | Nessuno | La directory di lavoro corrente |
| Senza argomenti | '.' | La directory di lavoro corrente |
| Uso tipico | Costruire un percorso a partire dalle parti | Trasformare un input dell'utente o una config in un percorso assoluto |
basename, dirname, extname e parse
Queste funzioni scompongono un percorso. Guardano solo la stringa: il file non deve per forza esistere.
Ha stampato cat.final.jpg, cat.final, /home/ada/photos e .jpg: extname prende solo la parte dopo l'ultimo punto. path.parse restituisce tutto in una volta (root, dir, base, ext, name), e path.format ritrasforma un oggetto del genere in una stringa.
__dirname e __filename
In un file CommonJS, __dirname è il percorso assoluto della cartella che contiene il file, e __filename quello del file stesso. Un percorso relativo come 'data.json' è relativo al punto da cui l'utente ha eseguito node, e si rompe appena qualcuno avvia lo script da un'altra cartella. Costruisci invece i percorsi a partire da __dirname:
Ha stampato config.json dark e index.js. I percorsi completi cambiano su ogni macchina, ed è proprio per questo che li costruisci con path.join(__dirname, ...) invece di scriverli a mano.
__dirname nei moduli ES: import.meta.dirname
I moduli ES non hanno __dirname; usarlo lancia ReferenceError: __dirname is not defined in ES module scope. Da Node.js 20.11 li sostituiscono import.meta.dirname e import.meta.filename:
Ha stampato index.js e data/users.json. Nelle versioni di Node.js precedenti alla 20.11, ricava la cartella dall'URL del modulo: path.dirname(fileURLToPath(import.meta.url)), con fileURLToPath da node:url.
Perché non unire i percorsi con le stringhe
'data' + '/' + file funziona su macOS e Linux, poi fallisce su Windows, dove il separatore è \, e non fa niente per .., barre doppie o una barra mancante. path usa le regole del sistema su cui gira. Puoi vedere il risultato di Windows da qualsiasi macchina con path.win32:
const path = require('path');
path.posix.join('users', 'ada', 'notes.txt'); // 'users/ada/notes.txt'
path.win32.join('users', 'ada', 'notes.txt'); // 'users\\ada\\notes.txt'
path.sep; // '/' on macOS and Linux, '\\' on Windows
Passa il risultato di path.join a fs.readFile e lo stesso codice legge il file su ogni sistema.
Errori comuni
Aspettarsi che extname conosca le doppie estensioni. path.extname('archive.tar.gz') è '.gz', e un file nascosto come .env non ha proprio estensione: path.extname('.env') è ''. Controlla tu la fine del nome quando ti serve il suffisso completo.
Contare su join per tenere un utente dentro una cartella. path.join('/srv/app', '../../etc/passwd') è '/etc/passwd'. Prima di leggere un file indicato da una richiesta, risolvi il percorso e controlla che inizi ancora con la tua cartella.
Domande frequenti
Che differenza c'è tra __dirname e process.cwd()?
__dirname è la cartella del file sorgente corrente e non cambia mai. process.cwd() è la cartella da cui l'utente ha eseguito node. Coincidono solo quando il programma viene avviato dalla sua cartella.
Con __dirname conviene path.join o path.resolve?
Per path.join(__dirname, 'data.json') vanno bene entrambi, perché __dirname è già assoluto. Usa resolve quando un segmento potrebbe essere a sua volta assoluto, come un percorso scritto dall'utente, e vuoi che sia quel percorso a vincere.
Come uso il modulo path con gli URL?
Non farlo: gli URL usano sempre / e hanno regole proprie per .. e le query string. Usa la classe URL, come in new URL('../img/logo.png', 'https://example.com/docs/page').