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

package.json exports: advanced

Поле exports — мощный механизм инкапсуляции публичного API пакета с поддержкой условных экспортов, subpath patterns и блокировкой internal paths; знание нюансов обязательно при публикации npm-пакетов.

// Полная структура exports для dual ESM/CJS пакета
// package.json:
// {
// "name": "my-lib",
// "version": "1.0.0",
// "type": "module",
// "main": "./dist/index.cjs", // legacy для старых инструментов
// "exports": {
// ".": {
// "types": "./dist/index.d.ts", // TypeScript — ПЕРВЫМ!
// "import": "./dist/index.mjs", // ESM
// "require": "./dist/index.cjs", // CJS
// "default": "./dist/index.mjs" // fallback
// },
// "./utils": {
// "types": "./dist/utils.d.ts",
// "import": "./dist/utils.mjs",
// "require": "./dist/utils.cjs"
// },
// "./package.json": "./package.json" // разрешаем чтение package.json
// }
// }
// Subpath patterns: glob * (Node.js 16+)
// "exports": {
// "./features/*": {
// "import": "./dist/features/*.mjs",
// "require": "./dist/features/*.cjs",
// "types": "./dist/features/*.d.ts"
// }
// }
// import { auth } from 'my-lib/features/auth'
// → ./dist/features/auth.mjs
// Блокировка internal paths: главное преимущество exports
// Без записи в exports → ERR_PACKAGE_PATH_NOT_EXPORTED
// import 'my-lib/src/internal-helper' // ← ошибка даже если файл есть
// "imports" field: self-references ВНУТРИ пакета
// "imports": {
// "#utils": "./src/utils.mjs",
// "#config": {
// "node": "./src/config.node.mjs",
// "default": "./src/config.browser.mjs"
// }
// }
// import { fn } from '#utils'; // работает только внутри пакета
// Кастомные условия и порядок (ВАЖЕН!)
// "exports": {
// ".": {
// "types": "./dist/index.d.ts", // TypeScript ПЕРВЫМ
// "development": "./dist/dev.mjs", // --conditions=development
// "production": "./dist/prod.mjs",
// "import": "./dist/index.mjs",
// "require": "./dist/index.cjs",
// "default": "./dist/index.mjs" // ПОСЛЕДНИМ
// }
// }
// webpack.config.js — подключить кастомные условия:
// resolve: { conditionNames: ['development', 'import', 'module', 'default'] }
// Node.js CLI:
// node --conditions=development app.js
// Типичные ошибки:
// 1. "types" не первым → tsc берёт неверный .d.ts (по условию "import")
// 2. Нет "default" → некоторые инструменты падают
// 3. "module" вместо "exports"."import" → bundler OK, Node.js игнорирует

Итог: exports — единственный правильный способ публиковать dual CJS/ESM пакет с TypeScript types; subpath exports блокируют доступ к internal путям и формируют стабильный публичный API.