Модуль path — работа с путями

Зачем нужно

Пути к файлам на Windows (C:\Users\admin\file.txt) и Unix (/home/user/file.txt) различаются разделителями и форматом. Модуль path строит и разбирает пути кроссплатформенно. Без него код работает на одной ОС и ломается на другой.

Где используется

  • Построение путей к файлам и директориям (fs-операции)
  • Извлечение имени файла, расширения, директории
  • Resolve относительных путей в абсолютные
  • Конфигурация сборщиков (Webpack, Vite)
  • CLI-инструменты: работа с путями пользователя

Предпосылки

  • Что такое Node.js — модульная система Node.js
  • Понимание файловой системы (абсолютные vs относительные пути)

Импорт

// CommonJS
const path = require('path');

// ESM
import path from 'path';

path.join — соединение сегментов

const path = require('path');

// Соединяет сегменты с правильным разделителем для ОС
path.join('src', 'utils', 'helpers.js');
// Linux/macOS: 'src/utils/helpers.js'
// Windows:     'src\\utils\\helpers.js'

// Нормализует лишние разделители и ../
path.join('src', '..', 'dist', 'bundle.js');
// 'dist/bundle.js'

path.join('/home', 'user', '..', 'admin', 'file.txt');
// '/home/admin/file.txt'

// ❌ Не делай так — ломается на Windows
const bad = 'src' + '/' + 'file.js';

// ✅ Всегда join
const good = path.join('src', 'file.js');

path.resolve — абсолютный путь

const path = require('path');

// resolve строит АБСОЛЮТНЫЙ путь
// Обрабатывает аргументы СПРАВА НАЛЕВО,
// останавливается когда получит абсолютный путь

path.resolve('src', 'index.js');
// '/home/user/project/src/index.js'  (от cwd)

path.resolve('/home/user', 'docs', 'readme.md');
// '/home/user/docs/readme.md'

// Если встречает абсолютный путь — начинает от него
path.resolve('src', '/tmp', 'file.txt');
// '/tmp/file.txt'  (абсолютный /tmp перекрыл src)

// Без аргументов — возвращает cwd
path.resolve;
// '/home/user/project'  (= process.cwd)

join vs resolve

const path = require('path');

// join: просто соединяет сегменты
path.join('/foo', 'bar', 'baz');      // '/foo/bar/baz'

// resolve: строит абсолютный путь от cwd
path.resolve('foo', 'bar', 'baz');    // '/home/user/project/foo/bar/baz'

// Главное отличие — resolve всегда возвращает абсолютный путь
path.join('foo', 'bar');              // 'foo/bar' (относительный)
path.resolve('foo', 'bar');           // '/home/user/project/foo/bar' (абсолютный)

Разбор пути

basename — имя файла

const path = require('path');

path.basename('/home/user/docs/report.pdf');
// 'report.pdf'

// Без расширения
path.basename('/home/user/docs/report.pdf', '.pdf');
// 'report'

path.basename('C:\\Users\\admin\\file.txt');
// 'file.txt' (работает и с Windows-путями)

dirname — директория

const path = require('path');

path.dirname('/home/user/docs/report.pdf');
// '/home/user/docs'

path.dirname('/home/user/docs/');
// '/home/user/docs'  (trailing slash — та же директория)

path.dirname('src/utils/helpers.js');
// 'src/utils'

extname — расширение

const path = require('path');

path.extname('report.pdf');      // '.pdf'
path.extname('archive.tar.gz');  // '.gz'  (только последнее)
path.extname('.gitignore');      // ''     (нет расширения)
path.extname('README');          // ''     (нет расширения)
path.extname('photo.JPG');       // '.JPG' (регистр сохраняется)

parse — полный разбор

const path = require('path');

const parsed = path.parse('/home/user/docs/report.pdf');
// {
//   root: '/',
//   dir: '/home/user/docs',
//   base: 'report.pdf',
//   ext: '.pdf',
//   name: 'report'
// }

// Обратная операция — format
path.format({
  dir: '/home/user/docs',
  name: 'report',
  ext: '.pdf'
});
// '/home/user/docs/report.pdf'

Утилиты

sep — разделитель ОС

const path = require('path');

path.sep;
// Linux/macOS: '/'
// Windows:     '\\'

// Разбить путь на сегменты
'src/utils/helpers.js'.split(path.sep);
// Linux: ['src', 'utils', 'helpers.js']

normalize — нормализация

const path = require('path');

// Убирает лишние разделители и разрешает ..
path.normalize('/home//user/../admin/./docs');
// '/home/admin/docs'

path.normalize('src\\\\utils\\..\\lib');
// 'src/lib' (Linux) или 'src\\lib' (Windows)

isAbsolute — проверка абсолютного пути

const path = require('path');

path.isAbsolute('/home/user');    // true
path.isAbsolute('C:\\Users');     // true  (Windows)
path.isAbsolute('./src');         // false
path.isAbsolute('src/index.js');  // false

relative — относительный путь между двумя

const path = require('path');

path.relative('/home/user/project', '/home/user/docs');
// '../docs'

path.relative('/home/user/project/src', '/home/user/project/dist');
// '../dist'

__dirname vs import.meta.url

// === CommonJS ===
// __dirname и __filename доступны напрямую

const path = require('path');
console.log(__dirname);   // '/home/user/project/src'
console.log(__filename);  // '/home/user/project/src/index.js'

const configPath = path.join(__dirname, '..', 'config.json');

// === ES Modules ===
// __dirname и __filename НЕ существуют
// Нужно получать из import.meta.url

import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// import.meta.url:
// 'file:///home/user/project/src/index.mjs'
// fileURLToPath → '/home/user/project/src/index.mjs'

const configPath = path.join(__dirname, '..', 'config.json');

Практические примеры

Фильтрация файлов по расширению

const fs = require('fs/promises');
const path = require('path');

async function findByExtension(dir, ext) {
  const entries = await fs.readdir(dir, { withFileTypes: true });
  const results = ;

  for (const entry of entries) {
    const fullPath = path.join(dir, entry.name);
    if (entry.isDirectory) {
      results.push(...await findByExtension(fullPath, ext));
    } else if (path.extname(entry.name) === ext) {
      results.push(fullPath);
    }
  }
  return results;
}

// Найти все .js файлы
const jsFiles = await findByExtension('./src', '.js');
console.log(jsFiles);
// ['src/index.js', 'src/utils/helpers.js', 'src/lib/db.js']

Безопасная работа с пользовательскими путями

const path = require('path');

// ❌ ОПАСНО: пользователь может выйти за пределы директории
function unsafeServefile(userPath) {
  return path.join('/var/www/public', userPath);
  // userPath = '../../etc/passwd' → '/var/www/etc/passwd'
}

// ✅ БЕЗОПАСНО: проверяем что путь остаётся внутри базовой директории
function safeServeFile(baseDir, userPath) {
  const resolved = path.resolve(baseDir, userPath);
  const normalized = path.normalize(resolved);

  if (!normalized.startsWith(path.normalize(baseDir))) {
    throw new Error('Path traversal detected');
  }
  return normalized;
}

safeServeFile('/var/www/public', 'images/logo.png');
// '/var/www/public/images/logo.png' ✅

safeServeFile('/var/www/public', '../../etc/passwd');
// Error: Path traversal detected ✅

Webpack-конфигурация

const path = require('path');

module.exports = {
  entry: path.resolve(__dirname, 'src', 'index.js'),
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src'),
      '@utils': path.resolve(__dirname, 'src', 'utils')
    }
  }
};

Частые ошибки

  1. Конкатенация строк вместо joindir + '/' + file ломается на Windows. Всегда path.join
  2. Путают join и resolvejoin возвращает относительный путь, resolve — абсолютный
  3. __dirname в ESM — не существует, нужно fileURLToPath(import.meta.url) + path.dirname
  4. Не нормализуют пользовательский ввод — path traversal атака (../../etc/passwd)
  5. Хардкод разделителя'src\\utils' работает только на Windows

Практика

  1. Написать функцию, возвращающую расширение файла в нижнем регистре (.JPG.jpg)
  2. Построить абсолютный путь к файлу config.json в родительской директории
  3. Написать функцию safeJoin(base, userInput), защищённую от path traversal
  4. Реализовать рекурсивный поиск всех файлов с заданным расширением
  5. Написать ESM-модуль с корректным получением __dirname

Связанные темы

  • fs — файловые операции (используют path для путей)
  • process — process.cwd и рабочая директория
  • http — раздача статических файлов

Ресурсы