path.join() assemble des segments de chemin avec le bon séparateur et nettoie les .. et les barres obliques en double, mais garde un résultat relatif si vous êtes parti d'un segment relatif. path.resolve() fait le même travail de droite à gauche et renvoie toujours un chemin absolu : il part du répertoire de travail courant, et tout segment absolu rencontré en chemin fait repartir le chemin de zéro. Utilisez join pour construire un chemin, et resolve quand il vous faut un chemin absolu.
Le programme a affiché users/ada/notes.txt, puis users/grace/todo.txt (le .. a retiré ada), puis /srv/app/public/index.html et src/lib/utils.js : les barres obliques en trop et les segments vides ont disparu. join ne traite jamais /public/ comme un nouveau départ ; c'est juste le segment suivant.
path.join ou path.resolve
La différence apparaît quand un segment commence par / ou quand le premier segment est relatif :
Avec /public au milieu, join a produit /srv/app/public/img alors que resolve a jeté tout ce qui précédait et produit /public/img ; les deux ont traité .. de la même façon. Les deux dernières lignes montrent que join a gardé public/index.html relatif, alors que resolve l'a rendu absolu en plaçant le dossier courant devant.
path.join | path.resolve | |
|---|---|---|
| Résultat | Relatif ou absolu, comme l'entrée | Toujours absolu |
Un segment qui commence par / | Un segment comme un autre | Fait repartir le chemin à cet endroit |
| Point de départ | Aucun | Le répertoire de travail courant |
| Sans argument | '.' | Le répertoire de travail courant |
| Usage courant | Construire un chemin à partir de morceaux | Transformer une saisie utilisateur ou une config en chemin absolu |
basename, dirname, extname et parse
Ces fonctions décomposent un chemin. Elles ne regardent que la chaîne : le fichier n'a pas besoin d'exister.
Le programme a affiché cat.final.jpg, cat.final, /home/ada/photos et .jpg : extname ne prend que la partie après le dernier point. path.parse renvoie tout d'un coup (root, dir, base, ext, name), et path.format retransforme un tel objet en chaîne.
__dirname et __filename
Dans un fichier CommonJS, __dirname est le chemin absolu du dossier qui contient le fichier, et __filename celui du fichier lui-même. Un chemin relatif comme 'data.json' est relatif à l'endroit où l'utilisateur a lancé node, ce qui casse dès que quelqu'un lance votre script depuis un autre dossier. Construisez plutôt les chemins à partir de __dirname :
Le programme a affiché config.json dark et index.js. Les chemins complets diffèrent sur chaque machine, et c'est justement pour cela qu'on les construit avec path.join(__dirname, ...) au lieu de les taper.
__dirname dans les modules ES : import.meta.dirname
Les modules ES n'ont pas de __dirname ; l'utiliser lève ReferenceError: __dirname is not defined in ES module scope. Depuis Node.js 20.11, import.meta.dirname et import.meta.filename les remplacent :
Le programme a affiché index.js et data/users.json. Avec les versions de Node.js antérieures à 20.11, déduisez le dossier de l'URL du module : path.dirname(fileURLToPath(import.meta.url)), avec fileURLToPath de node:url.
Pourquoi ne pas assembler les chemins avec des chaînes
'data' + '/' + file marche sur macOS et Linux, puis échoue sous Windows, où le séparateur est \, et ne gère ni les .., ni les barres en double, ni une barre manquante. path applique les règles du système sur lequel il tourne. Vous pouvez voir le résultat Windows depuis n'importe quelle machine avec 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
Passez le résultat de path.join à fs.readFile et le même code lit le fichier sur tous les systèmes.
Erreurs fréquentes
S'attendre à ce qu'extname connaisse les doubles extensions. path.extname('archive.tar.gz') vaut '.gz', et un fichier caché comme .env n'a pas d'extension du tout : path.extname('.env') vaut ''. Vérifiez vous-même la fin du nom quand le suffixe complet compte.
Compter sur join pour garder un utilisateur dans un dossier. path.join('/srv/app', '../../etc/passwd') vaut '/etc/passwd'. Avant de lire un fichier nommé par une requête, résolvez le chemin et vérifiez qu'il commence toujours par votre dossier.
Questions fréquentes
Quelle différence entre __dirname et process.cwd() ?
__dirname est le dossier du fichier source courant et ne change jamais. process.cwd() est le dossier depuis lequel l'utilisateur a lancé node. Ils ne sont identiques que si le programme est lancé depuis son propre dossier.
Faut-il utiliser path.join ou path.resolve avec __dirname ?
Les deux marchent pour path.join(__dirname, 'data.json'), puisque __dirname est déjà absolu. Utilisez resolve quand un segment peut lui-même être absolu, comme un chemin saisi par l'utilisateur, et que vous voulez que ce chemin l'emporte.
Comment utiliser le module path avec des URL ?
Ne le faites pas : les URL utilisent toujours / et ont leurs propres règles pour .. et les query strings. Utilisez la classe URL, comme dans new URL('../img/logo.png', 'https://example.com/docs/page').