path.join() junta segmentos de caminho com o separador certo e limpa .. e barras duplicadas, mas mantém o resultado relativo se você começou com um segmento relativo. path.resolve() faz o mesmo trabalho da direita para a esquerda e sempre retorna um caminho absoluto: ele parte do diretório de trabalho atual, e qualquer segmento absoluto no meio do caminho recomeça o caminho. Use join para montar um caminho, e resolve quando precisar de um absoluto.
Ele imprimiu users/ada/notes.txt, depois users/grace/todo.txt (o .. removeu ada), depois /srv/app/public/index.html e src/lib/utils.js: barras extras e segmentos vazios sumiram. O join nunca trata /public/ como um novo começo; é só o próximo segmento.
path.join vs path.resolve
A diferença aparece quando um segmento começa com / ou quando o primeiro segmento é relativo:
Com /public no meio, o join produziu /srv/app/public/img, enquanto o resolve descartou tudo antes dele e produziu /public/img; os dois trataram .. do mesmo jeito. As duas últimas linhas mostram que o join manteve public/index.html relativo, enquanto o resolve o tornou absoluto colocando a pasta atual na frente.
path.join | path.resolve | |
|---|---|---|
| Resultado | Relativo ou absoluto, como a entrada | Sempre absoluto |
Um segmento que começa com / | Só mais um segmento | Recomeça o caminho ali |
| Ponto de partida | Nenhum | O diretório de trabalho atual |
| Sem argumentos | '.' | O diretório de trabalho atual |
| Uso típico | Montar um caminho a partir de partes | Transformar entrada do usuário ou config em caminho absoluto |
basename, dirname, extname e parse
Estas funções desmontam um caminho. Elas só olham a string: o arquivo não precisa existir.
Ele imprimiu cat.final.jpg, cat.final, /home/ada/photos e .jpg: extname pega só a parte depois do último ponto. path.parse retorna tudo de uma vez (root, dir, base, ext, name), e path.format transforma um objeto desses de volta em string.
__dirname e __filename
Em um arquivo CommonJS, __dirname é o caminho absoluto da pasta que contém o arquivo, e __filename é o próprio arquivo. Um caminho relativo como 'data.json' é relativo ao lugar de onde o usuário rodou node, o que quebra assim que alguém inicia seu script de outra pasta. Monte os caminhos a partir de __dirname:
Ele imprimiu config.json dark e index.js. Os caminhos completos mudam em cada máquina, e é exatamente por isso que você os monta com path.join(__dirname, ...) em vez de digitá-los.
__dirname em ES modules: import.meta.dirname
ES modules não têm __dirname; usar lança ReferenceError: __dirname is not defined in ES module scope. Desde o Node.js 20.11, import.meta.dirname e import.meta.filename substituem os dois:
Ele imprimiu index.js e data/users.json. Em versões do Node.js anteriores à 20.11, obtenha a pasta a partir da URL do módulo: path.dirname(fileURLToPath(import.meta.url)), com fileURLToPath vindo de node:url.
Por que não juntar caminhos com strings
'data' + '/' + file funciona no macOS e no Linux e depois falha no Windows, onde o separador é \, e não resolve nada sobre .., barras duplicadas ou uma barra faltando. O path usa as regras do sistema em que está rodando. Você pode ver o resultado do Windows em qualquer máquina com 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
Passe o resultado de path.join para o fs.readFile e o mesmo código lê o arquivo em qualquer sistema.
Erros comuns
Esperar que extname entenda extensões duplas. path.extname('archive.tar.gz') é '.gz', e um dotfile como .env não tem extensão nenhuma: path.extname('.env') é ''. Verifique o fim do nome por conta própria quando o sufixo completo importar.
Confiar no join para manter o usuário dentro de uma pasta. path.join('/srv/app', '../../etc/passwd') é '/etc/passwd'. Antes de ler um arquivo cujo nome veio de uma requisição, resolva o caminho e confira se ele ainda começa com a sua pasta.
Perguntas frequentes
Qual a diferença entre __dirname e process.cwd()?
__dirname é a pasta do arquivo de código atual e nunca muda. process.cwd() é a pasta de onde o usuário rodou node. Os dois só são iguais quando o programa é iniciado da própria pasta.
Devo usar path.join ou path.resolve com __dirname?
Os dois funcionam para path.join(__dirname, 'data.json'), já que __dirname já é absoluto. Use resolve quando um segmento puder ser absoluto por conta própria, como um caminho digitado pelo usuário, e você quiser que esse caminho prevaleça.
Como uso o módulo path com URLs?
Não use: URLs sempre usam / e têm suas próprias regras para .. e query strings. Use a classe URL, como em new URL('../img/logo.png', 'https://example.com/docs/page').