Перейти к содержимому

Module Resolution

Алгоритм разрешения модулей в Node.js, TypeScript и bundler-ах различается принципиально; путаница с полями exports, main, module, расширениями файлов и conditions — источник 80% проблем при публикации npm-пакетов.

// Node.js ESM resolution (строгий):
// 1. Bare specifier: 'lodash' → node_modules + "exports" field
// 2. Relative path: './utils.js' → расширение ОБЯЗАТЕЛЬНО!
// 3. Absolute URL: 'file:///…'
// Node.js CJS resolution (мягкий):
// 1. require('lodash') → node_modules/lodash/package.json → "main"
// 2. require('./utils') → utils.js → utils/index.js → utils.json
// (автоматически добавляет .js / /index.js — в ESM этого нет!)
// TypeScript добавляет свои правила:
// "moduleResolution": "bundler" | "node16" | "nodenext" | "classic"
// В node16/nodenext: .ts файл импортируется через .js расширение!
// import { fn } from './utils.js'; // ← .js, не .ts — это правильно
// package.json "exports" field: приоритет над "main"
// {
// "main": "./dist/index.cjs", // legacy, если нет exports
// "module": "./dist/index.esm.js", // НЕ стандарт Node.js, только bundler hint
// "exports": {
// ".": {
// "import": "./dist/index.mjs", // ESM
// "require": "./dist/index.cjs", // CJS
// "types": "./dist/index.d.ts" // TypeScript
// },
// "./utils": {
// "import": "./dist/utils.mjs",
// "require": "./dist/utils.cjs"
// }
// }
// }
// Если "exports" определён → ТОЛЬКО перечисленные пути доступны
// import 'lib/src/internal' → ERR_PACKAGE_PATH_NOT_EXPORTED (капсуляция!)
// Conditions: Node.js и bundler применяют разные наборы
// Node.js встроенные: "import", "require", "node", "default"
// Пользовательские: node --conditions=production app.js
// Webpack: conditionNames: ['browser', 'import', 'module', 'default']
// Vite: conditionNames: ['browser', 'module', 'import', 'default']
// Subpath patterns (Node 16+):
// "exports": {
// "./features/*": {
// "import": "./dist/features/*.mjs",
// "require": "./dist/features/*.cjs"
// }
// }
// import 'my-lib/features/auth' → ./dist/features/auth.mjs
// "imports" (self-referencing внутри пакета):
// "imports": { "#utils": "./src/utils.mjs" }
// → import '#utils' внутри пакета (не экспортируется наружу)

Итог: Module resolution — многоуровневый алгоритм, зависящий от типа модуля (CJS/ESM), среды (Node/bundler) и конфигурации exports; правильная настройка exports обязательна для dual-package публикации.