Skip to content

Модули: Пакеты

[История]

ВерсияИзменения
v14.13.0, v12.20.0Добавлена поддержка шаблонов "exports".
v14.6.0, v12.19.0Добавлено поле "imports" пакета.
v13.7.0, v12.17.0Сняты флаги условного экспорта.
v13.7.0, v12.16.0Удалена опция --experimental-conditional-exports. В версии 12.16.0 условный экспорт по-прежнему находится за флагом --experimental-modules.
v13.6.0, v12.16.0Снят флаг самоссылки на пакет с использованием его имени.
v12.7.0Представлено поле "exports" в package.json в качестве более мощной альтернативы классическому полю "main".
v12.0.0Добавлена поддержка ES-модулей, использующих расширение файла .js, через поле "type" в package.json.

Введение

Пакет — это древовидная структура каталогов, описанная файлом package.json. Пакет состоит из каталога, содержащего файл package.json, и всех подкаталогов до следующего каталога, содержащего другой файл package.json, или каталога с именем node_modules.

Эта страница содержит рекомендации для авторов пакетов, пишущих файлы package.json, а также справочник по полям package.json, определённым Node.js.

Определение модульной системы

Введение

Node.js будет обрабатывать следующее как ES-модули, когда передаётся в node в качестве начального ввода, или когда на них ссылаются операторы import или выражения import():

  • Файлы с расширением .mjs.
  • Файлы с расширением .js, когда ближайший родительский файл package.json содержит поле верхнего уровня "type" со значением "module".
  • Строки, переданные в качестве аргумента в --eval или переданные в node через STDIN с флагом --input-type=module.
  • Код, содержащий синтаксис, который успешно разбирается только как ES-модули, например, операторы import или export или import.meta, без явного указания того, как его следует интерпретировать. Явные маркеры — это расширения .mjs или .cjs, поля "type" в package.json со значениями "module" или "commonjs" или флаг --input-type. Динамические выражения import() поддерживаются как в CommonJS, так и в ES-модулях и не заставят файл обрабатываться как ES-модуль. См. Определение синтаксиса.

Node.js будет обрабатывать следующее как CommonJS, когда передаётся в node в качестве начального ввода, или когда на них ссылаются операторы import или выражения import():

  • Файлы с расширением .cjs.
  • Файлы с расширением .js, когда ближайший родительский файл package.json содержит поле верхнего уровня "type" со значением "commonjs".
  • Строки, переданные в качестве аргумента в --eval или --print, или переданные в node через STDIN с флагом --input-type=commonjs.
  • Файлы с расширением .js без родительского файла package.json или где ближайший родительский файл package.json не имеет поля type, и где код может быть успешно оценён как CommonJS. Другими словами, Node.js сначала пытается запустить такие «двусмысленные» файлы как CommonJS, и повторно пытается оценить их как ES-модули, если оценка как CommonJS завершается неудачей, поскольку анализатор обнаружил синтаксис ES-модуля.

Написание синтаксиса ES-модуля в «двусмысленных» файлах влечёт за собой снижение производительности, поэтому рекомендуется, чтобы авторы по возможности были явными. В частности, авторы пакетов всегда должны включать поле "type" в свои файлы package.json, даже в пакетах, где все источники являются CommonJS. Явное указание типа пакета обезопасит пакет на случай, если тип Node.js по умолчанию когда-либо изменится, а также облегчит работу инструментам сборки и загрузчикам при определении того, как следует интерпретировать файлы в пакете.

Обнаружение синтаксиса

[История]

ВерсияИзменения
v22.7.0Обнаружение синтаксиса включено по умолчанию.
v21.1.0, v20.10.0Добавлено в: v21.1.0, v20.10.0

[Стабильно: 1 - Экспериментально]

Стабильно: 1 Стабильность: 1.2 - Кандидат на выпуск

Node.js будет анализировать исходный код неоднозначного ввода, чтобы определить, содержит ли он синтаксис модуля ES; если такой синтаксис обнаружен, ввод будет рассматриваться как модуль ES.

Неоднозначный ввод определяется как:

  • Файлы с расширением .js или без расширения; и либо без управляющего файла package.json, либо с файлом, в котором отсутствует поле type.
  • Строковый ввод (--eval или STDIN), когда --input-type не указан.

Синтаксис модуля ES определяется как синтаксис, который выдаст ошибку при оценке как CommonJS. Это включает в себя следующее:

  • Операторы import (но не выражения import(), которые допустимы в CommonJS).
  • Операторы export.
  • Ссылки на import.meta.
  • await на верхнем уровне модуля.
  • Лексические переобъявления переменных-обёрток CommonJS (require, module, exports, __dirname, __filename).

Загрузчики модулей

Node.js имеет две системы для разрешения спецификатора и загрузки модулей.

Существует загрузчик модулей CommonJS:

  • Он полностью синхронный.
  • Он отвечает за обработку вызовов require().
  • Его можно исправлять (monkey patchable).
  • Он поддерживает папки как модули.
  • При разрешении спецификатора, если точное соответствие не найдено, он попытается добавить расширения (.js, .json и, наконец, .node), а затем попытается разрешить папки как модули.
  • Он рассматривает .json как текстовые файлы JSON.
  • Файлы .node интерпретируются как скомпилированные модули-дополнения, загруженные с помощью process.dlopen().
  • Он рассматривает все файлы без расширений .json или .node как текстовые файлы JavaScript.
  • Он может использоваться только для загрузки модулей ECMAScript из модулей CommonJS, если граф модулей синхронный (то есть не содержит await верхнего уровня). При использовании для загрузки текстового файла JavaScript, который не является модулем ECMAScript, файл будет загружен как модуль CommonJS.

Существует загрузчик модулей ECMAScript:

  • Он асинхронный, если только он не используется для загрузки модулей для require().
  • Он отвечает за обработку операторов import и выражений import().
  • Его нельзя исправлять (monkey patchable), его можно настроить с помощью хуков загрузчика.
  • Он не поддерживает папки как модули, индексы каталогов (например, './startup/index.js') должны быть указаны полностью.
  • Он не выполняет поиск расширений. Расширение файла должно быть указано, когда спецификатор является относительным или абсолютным URL-адресом файла.
  • Он может загружать модули JSON, но требуется атрибут типа импорта.
  • Он принимает только расширения .js, .mjs и .cjs для текстовых файлов JavaScript.
  • Он может использоваться для загрузки модулей JavaScript CommonJS. Такие модули передаются через cjs-module-lexer, чтобы попытаться идентифицировать именованные экспорты, которые доступны, если их можно определить с помощью статического анализа. URL-адреса импортированных модулей CommonJS преобразуются в абсолютные пути, а затем загружаются через загрузчик модулей CommonJS.

package.json и расширения файлов

Внутри пакета поле "type" в package.json определяет, как Node.js должен интерпретировать .js файлы. Если в файле package.json отсутствует поле "type", .js файлы обрабатываются как CommonJS.

Значение "module" поля "type" в package.json указывает Node.js интерпретировать .js файлы внутри этого пакета, используя синтаксис ES module.

Поле "type" применяется не только к начальным точкам входа (node my-app.js), но и к файлам, на которые ссылаются операторы import и выражения import().

js
// my-app.js, обрабатывается как ES модуль, потому что в той же папке
// находится файл package.json с "type": "module".

import './startup/init.js';
// Загружается как ES модуль, поскольку ./startup не содержит файл package.json,
// и, следовательно, наследует значение "type" с одного уровня выше.

import 'commonjs-package';
// Загружается как CommonJS, поскольку ./node_modules/commonjs-package/package.json
// не имеет поля "type" или содержит "type": "commonjs".

import './node_modules/commonjs-package/index.js';
// Загружается как CommonJS, поскольку ./node_modules/commonjs-package/package.json
// не имеет поля "type" или содержит "type": "commonjs".

Файлы, заканчивающиеся на .mjs, всегда загружаются как ES modules независимо от ближайшего родительского package.json.

Файлы, заканчивающиеся на .cjs, всегда загружаются как CommonJS независимо от ближайшего родительского package.json.

js
import './legacy-file.cjs';
// Загружается как CommonJS, поскольку .cjs всегда загружается как CommonJS.

import 'commonjs-package/src/index.mjs';
// Загружается как ES модуль, поскольку .mjs всегда загружается как ES модуль.

Расширения .mjs и .cjs можно использовать для смешивания типов внутри одного пакета:

  • В пакете с "type": "module" Node.js может быть настроен для интерпретации конкретного файла как CommonJS, присвоив ему расширение .cjs (поскольку файлы .js и .mjs обрабатываются как ES модули внутри пакета "module").
  • В пакете с "type": "commonjs" Node.js может быть настроен для интерпретации конкретного файла как ES module, присвоив ему расширение .mjs (поскольку файлы .js и .cjs обрабатываются как CommonJS внутри пакета "commonjs").

Флаг --input-type

Добавлено в: v12.0.0

Строки, переданные в качестве аргумента в --eval (или -e) или переданные в node через STDIN, обрабатываются как ES-модули, если установлен флаг --input-type=module.

bash
node --input-type=module --eval "import { sep } from 'node:path'; console.log(sep);"

echo "import { sep } from 'node:path'; console.log(sep);" | node --input-type=module

Для полноты также существует --input-type=commonjs для явного запуска строкового ввода как CommonJS. Это поведение по умолчанию, если --input-type не указан.

Определение менеджера пакетов

[Stable: 1 - Experimental]

Stable: 1 Стабильность: 1 - Экспериментально

Хотя все проекты Node.js должны быть устанавливаемыми всеми менеджерами пакетов после публикации, их командам разработчиков часто требуется использовать один конкретный менеджер пакетов. Чтобы упростить этот процесс, Node.js поставляется с инструментом под названием Corepack, который призван сделать все менеджеры пакетов прозрачно доступными в вашей среде - при условии, что у вас установлен Node.js.

По умолчанию Corepack не будет принудительно использовать какой-либо конкретный менеджер пакетов и будет использовать общие версии "Last Known Good", связанные с каждым выпуском Node.js, но вы можете улучшить этот опыт, установив поле "packageManager" в package.json вашего проекта.

Точки входа пакета

В файле package.json пакета два поля могут определять точки входа для пакета: "main" и "exports". Оба поля применяются как к ES-модулям, так и к модулям CommonJS.

Поле "main" поддерживается во всех версиях Node.js, но его возможности ограничены: оно определяет только основную точку входа пакета.

Поле "exports" предоставляет современную альтернативу "main", позволяющую определять несколько точек входа, поддерживать разрешение условных точек входа между средами и предотвращать любые другие точки входа, кроме тех, которые определены в "exports". Эта инкапсуляция позволяет авторам модулей четко определять общедоступный интерфейс для своего пакета.

Для новых пакетов, предназначенных для текущих поддерживаемых версий Node.js, рекомендуется использовать поле "exports". Для пакетов, поддерживающих Node.js 10 и ниже, требуется поле "main". Если определены и "exports", и "main", поле "exports" имеет приоритет над "main" в поддерживаемых версиях Node.js.

Условные экспорты могут использоваться в "exports" для определения различных точек входа пакета для каждой среды, включая то, как пакет вызывается: через require или через import. Для получения дополнительной информации о поддержке как CommonJS, так и ES-модулей в одном пакете, обратитесь к разделу о двойных пакетах CommonJS/ES-модулей.

Существующие пакеты, вводящие поле "exports", не позволят потребителям пакета использовать какие-либо точки входа, которые не определены, включая package.json (например, require('your-package/package.json')). Это скорее всего, будет критическим изменением.

Чтобы сделать введение "exports" некритическим, убедитесь, что экспортируется каждая ранее поддерживаемая точка входа. Лучше всего явно указывать точки входа, чтобы общедоступный API пакета был четко определен. Например, проект, который ранее экспортировал main, lib, feature и package.json, может использовать следующие package.exports:

json
{
  "name": "my-package",
  "exports": {
    ".": "./lib/index.js",
    "./lib": "./lib/index.js",
    "./lib/index": "./lib/index.js",
    "./lib/index.js": "./lib/index.js",
    "./feature": "./feature/index.js",
    "./feature/index": "./feature/index.js",
    "./feature/index.js": "./feature/index.js",
    "./package.json": "./package.json"
  }
}

В качестве альтернативы проект может экспортировать целые папки как с расширенными, так и без расширенных подпутей, используя шаблоны экспорта:

json
{
  "name": "my-package",
  "exports": {
    ".": "./lib/index.js",
    "./lib": "./lib/index.js",
    "./lib/*": "./lib/*.js",
    "./lib/*.js": "./lib/*.js",
    "./feature": "./feature/index.js",
    "./feature/*": "./feature/*.js",
    "./feature/*.js": "./feature/*.js",
    "./package.json": "./package.json"
  }
}

При этом обеспечивается обратная совместимость для любых младших версий пакета, а будущее основное изменение для пакета может правильно ограничить экспорты только определенными экспортируемыми функциями:

json
{
  "name": "my-package",
  "exports": {
    ".": "./lib/index.js",
    "./feature/*.js": "./feature/*.js",
    "./feature/internal/*": null
  }
}

Экспорт основной точки входа

При написании нового пакета рекомендуется использовать поле "exports":

json
{
  "exports": "./index.js"
}

Когда определено поле "exports", все подпути пакета инкапсулируются и становятся недоступными для импортеров. Например, require('pkg/subpath.js') вызывает ошибку ERR_PACKAGE_PATH_NOT_EXPORTED.

Эта инкапсуляция экспорта обеспечивает более надежные гарантии в отношении интерфейсов пакета для инструментов и при обработке semver-обновлений для пакета. Это не строгая инкапсуляция, поскольку прямой вызов любого абсолютного подпути пакета, например require('/path/to/node_modules/pkg/subpath.js'), все равно загрузит subpath.js.

Все поддерживаемые в настоящее время версии Node.js и современные инструменты сборки поддерживают поле "exports". Для проектов, использующих более старую версию Node.js или связанный инструмент сборки, совместимость может быть достигнута путем включения поля "main" вместе с "exports", указывающего на тот же модуль:

json
{
  "main": "./index.js",
  "exports": "./index.js"
}

Экспорт подпутей

Добавлено в: v12.7.0

При использовании поля "exports" пользовательские подпути могут быть определены вместе с основной точкой входа, рассматривая основную точку входа как подпуть ".":

json
{
  "exports": {
    ".": "./index.js",
    "./submodule.js": "./src/submodule.js"
  }
}

Теперь потребитель может импортировать только определенный подпуть в "exports":

js
import submodule from 'es-module-package/submodule.js';
// Загружает ./node_modules/es-module-package/src/submodule.js

В то время как другие подпути выдадут ошибку:

js
import submodule from 'es-module-package/private-module.js';
// Выбрасывает ERR_PACKAGE_PATH_NOT_EXPORTED

Расширения в подпутях

Авторам пакетов следует предоставлять подпути с расширениями (import 'pkg/subpath.js') или без расширений (import 'pkg/subpath') в своих экспортах. Это гарантирует, что для каждого экспортируемого модуля существует только один подпуть, чтобы все зависимые импортировали один и тот же согласованный спецификатор, сохраняя контракт пакета ясным для потребителей и упрощая завершение подпутей пакета.

Традиционно пакеты, как правило, использовали стиль без расширений, который имеет преимущества в удобочитаемости и маскировке истинного пути к файлу внутри пакета.

Поскольку карты импорта теперь предоставляют стандарт для разрешения пакетов в браузерах и других средах выполнения JavaScript, использование стиля без расширений может привести к раздутым определениям карт импорта. Явные расширения файлов могут избежать этой проблемы, позволяя карте импорта использовать сопоставление папок пакетов для сопоставления нескольких подпутей, где это возможно, вместо отдельной записи карты для каждого экспорта подпути пакета. Это также отражает требование использования полного пути спецификатора в относительных и абсолютных спецификаторах импорта.

Упрощение экспорта

Добавлено в версии: v12.11.0

Если экспорт "." является единственным экспортом, поле "exports" предоставляет упрощенный синтаксис, когда его значением является непосредственное значение поля "exports".

json
{
  "exports": {
    ".": "./index.js"
  }
}

Можно записать как:

json
{
  "exports": "./index.js"
}

Субпути импорта

Добавлено в версии: v14.6.0, v12.19.0

В дополнение к полю "exports", существует поле "imports" пакета для создания частных отображений, которые применяются только к спецификаторам импорта изнутри самого пакета.

Записи в поле "imports" всегда должны начинаться с #, чтобы отличать их от спецификаторов внешних пакетов.

Например, поле imports можно использовать для получения преимуществ условного экспорта для внутренних модулей:

json
// package.json
{
  "imports": {
    "#dep": {
      "node": "dep-node-native",
      "default": "./dep-polyfill.js"
    }
  },
  "dependencies": {
    "dep-node-native": "^1.0.0"
  }
}

где import '#dep' не получает разрешение внешнего пакета dep-node-native (включая, в свою очередь, его экспорты), а получает локальный файл ./dep-polyfill.js относительно пакета в других средах.

В отличие от поля "exports", поле "imports" разрешает отображение на внешние пакеты.

Правила разрешения для поля imports в остальном аналогичны полю exports.

Шаблоны субпутей

[История]

ВерсияИзменения
v16.10.0, v14.19.0Поддержка концевых частей шаблонов в поле "imports".
v16.9.0, v14.19.0Поддержка концевых частей шаблонов.
v14.13.0, v12.20.0Добавлено в версии: v14.13.0, v12.20.0

Для пакетов с небольшим количеством экспортов или импортов мы рекомендуем явно перечислять каждую запись субпути экспорта. Но для пакетов, у которых большое количество субпутей, это может привести к раздуванию package.json и проблемам с обслуживанием.

Для этих случаев можно использовать шаблоны экспорта субпутей:

json
// ./node_modules/es-module-package/package.json
{
  "exports": {
    "./features/*.js": "./src/features/*.js"
  },
  "imports": {
    "#internal/*.js": "./src/internal/*.js"
  }
}

* в отображениях раскрывает вложенные субпути, поскольку это синтаксис только замены строки.

Все экземпляры * в правой части затем будут заменены этим значением, в том числе если оно содержит какие-либо разделители /.

js
import featureX from 'es-module-package/features/x.js';
// Загружает ./node_modules/es-module-package/src/features/x.js

import featureY from 'es-module-package/features/y/y.js';
// Загружает ./node_modules/es-module-package/src/features/y/y.js

import internalZ from '#internal/z.js';
// Загружает ./node_modules/es-module-package/src/internal/z.js

Это прямое статическое сопоставление и замена без какой-либо специальной обработки расширений файлов. Включение "*.js" с обеих сторон сопоставления ограничивает экспорты пакета только файлами JS.

Свойство экспортов быть статически перечислимыми сохраняется с шаблонами экспортов, поскольку отдельные экспорты для пакета можно определить, рассматривая целевой шаблон правой части как глоб ** по отношению к списку файлов внутри пакета. Поскольку пути node_modules запрещены в целях экспорта, это расширение зависит только от файлов самого пакета.

Чтобы исключить частные подпапки из шаблонов, можно использовать цели null:

json
// ./node_modules/es-module-package/package.json
{
  "exports": {
    "./features/*.js": "./src/features/*.js",
    "./features/private-internal/*": null
  }
}
js
import featureInternal from 'es-module-package/features/private-internal/m.js';
// Выбрасывает: ERR_PACKAGE_PATH_NOT_EXPORTED

import featureX from 'es-module-package/features/x.js';
// Загружает ./node_modules/es-module-package/src/features/x.js

Условные экспорты

[История]

ВерсияИзменения
v13.7.0, v12.16.0Сняты флаги условных экспортов.
v13.2.0, v12.16.0Добавлено в: v13.2.0, v12.16.0

Условные экспорты предоставляют способ сопоставления с различными путями в зависимости от определенных условий. Они поддерживаются как для импорта CommonJS, так и для импорта модулей ES.

Например, пакет, который хочет предоставить разные экспорты модулей ES для require() и import, можно записать так:

json
// package.json
{
  "exports": {
    "import": "./index-module.js",
    "require": "./index-require.cjs"
  },
  "type": "module"
}

Node.js реализует следующие условия, перечисленные в порядке от наиболее конкретного к наименее конкретному, поскольку условия должны быть определены:

  • "node-addons" — аналогично "node" и соответствует любой среде Node.js. Это условие можно использовать для предоставления точки входа, которая использует собственные C++-аддоны, в отличие от точки входа, которая является более универсальной и не зависит от собственных аддонов. Это условие можно отключить с помощью флага --no-addons.
  • "node" — соответствует любой среде Node.js. Может быть файлом CommonJS или модулем ES. В большинстве случаев явно указывать платформу Node.js не обязательно.
  • "import" — соответствует, когда пакет загружается через import или import(), или через любую операцию импорта или разрешения верхнего уровня загрузчиком модулей ECMAScript. Применяется независимо от формата модуля целевого файла. Всегда взаимоисключает с "require".
  • "require" — соответствует, когда пакет загружается через require(). На файл, на который ссылаются, можно загрузить с помощью require(), хотя условие соответствует независимо от формата модуля целевого файла. Ожидаемые форматы включают CommonJS, JSON, собственные аддоны и модули ES. Всегда взаимоисключает с "import".
  • "module-sync" — соответствует независимо от того, загружается ли пакет через import, import() или require(). Ожидается, что формат будет модулем ES, который не содержит await верхнего уровня в своем графе модулей — если это так, будет выброшено ERR_REQUIRE_ASYNC_MODULE, когда модуль будет require()-ен.
  • "default" — универсальный резервный вариант, который всегда соответствует. Может быть файлом CommonJS или модулем ES. Это условие всегда должно быть последним.

Внутри объекта "exports" порядок ключей имеет значение. Во время сопоставления условий более ранние записи имеют более высокий приоритет и имеют приоритет над более поздними записями. Общее правило состоит в том, что условия должны быть от наиболее конкретных к наименее конкретным в порядке объектов.

Использование условий "import" и "require" может привести к некоторым опасностям, которые более подробно объяснены в разделе о двойных пакетах CommonJS/ES-модулей.

Условие "node-addons" можно использовать для предоставления точки входа, которая использует собственные C++-аддоны. Однако это условие можно отключить с помощью флага --no-addons. При использовании "node-addons" рекомендуется рассматривать "default" как улучшение, которое предоставляет более универсальную точку входа, например, с использованием WebAssembly вместо собственного аддона.

Условные экспорты также можно распространить на подпути экспорта, например:

json
{
  "exports": {
    ".": "./index.js",
    "./feature.js": {
      "node": "./feature-node.js",
      "default": "./feature.js"
    }
  }
}

Определяет пакет, где require('pkg/feature.js') и import 'pkg/feature.js') могут предоставлять различные реализации между Node.js и другими средами JS.

При использовании ветвей среды всегда включайте условие "default", где это возможно. Предоставление условия "default" гарантирует, что любые неизвестные среды JS смогут использовать эту универсальную реализацию, что помогает избежать того, чтобы эти среды JS притворялись существующими средами, чтобы поддерживать пакеты с условными экспортами. По этой причине использование ветвей условий "node" и "default" обычно предпочтительнее, чем использование ветвей условий "node" и "browser".

Вложенные условия

В дополнение к прямым соответствиям, Node.js также поддерживает вложенные объекты условий.

Например, чтобы определить пакет, который имеет только точки входа двойного режима для использования в Node.js, но не в браузере:

json
{
  "exports": {
    "node": {
      "import": "./feature-node.mjs",
      "require": "./feature-node.cjs"
    },
    "default": "./feature.mjs"
  }
}

Условия продолжают сопоставляться в том же порядке, что и плоские условия. Если вложенное условие не имеет никакого соответствия, оно продолжит проверять оставшиеся условия родительского условия. Таким образом, вложенные условия ведут себя аналогично вложенным операторам if в JavaScript.

Разрешение пользовательских условий

Добавлено в: v14.9.0, v12.19.0

При запуске Node.js пользовательские условия могут быть добавлены с помощью флага --conditions:

bash
node --conditions=development index.js

который затем разрешит условие "development" в импорте и экспорте пакетов, одновременно разрешая существующие условия "node", "node-addons", "default", "import" и "require" в соответствии с требованиями.

Любое количество пользовательских условий может быть установлено с помощью повторяющихся флагов.

Типичные условия должны содержать только буквенно-цифровые символы, используя ":", "-" или "=" в качестве разделителей, если это необходимо. Все остальное может привести к проблемам совместимости за пределами Node.

В Node условия имеют очень мало ограничений, но конкретно они включают:

Определения условий сообщества

Строки условий, отличные от условий "import", "require", "node", "module-sync", "node-addons" и "default", реализованных в ядре Node.js, по умолчанию игнорируются.

Другие платформы могут реализовывать другие условия, а пользовательские условия могут быть включены в Node.js с помощью флага --conditions / -C.

Поскольку пользовательские условия пакета требуют четких определений для обеспечения правильного использования, ниже приведен список общих известных условий пакета и их строгие определения, чтобы помочь в координации экосистемы.

  • "types" - может использоваться системами типизации для разрешения файла типизации для данного экспорта. Это условие всегда должно быть включено первым.
  • "browser" - любая среда веб-браузера.
  • "development" - может использоваться для определения точки входа только для среды разработки, например, для предоставления дополнительного контекста отладки, такого как улучшенные сообщения об ошибках при запуске в режиме разработки. Всегда должен быть взаимно исключающим с "production".
  • "production" - может использоваться для определения точки входа производственной среды. Всегда должен быть взаимно исключающим с "development".

Для других сред выполнения определения ключей условий, специфичных для платформы, поддерживаются WinterCG в спецификации предложения Runtime Keys.

Новые определения условий могут быть добавлены в этот список путем создания запроса на включение в документацию Node.js для этого раздела. Требования для внесения нового определения условия в список здесь следующие:

  • Определение должно быть четким и недвусмысленным для всех разработчиков.
  • Необходимо четко обосновать вариант использования, для чего необходимо условие.
  • Должно существовать достаточное количество существующих реализаций.
  • Имя условия не должно конфликтовать с другим определением условия или условием, широко используемым.
  • Перечисление определения условия должно обеспечивать координационное преимущество для экосистемы, которое в противном случае было бы невозможно. Например, это не обязательно относится к условиям, специфичным для компании или приложения.
  • Условие должно быть таким, чтобы пользователь Node.js ожидал его увидеть в основной документации Node.js. Условие "types" является хорошим примером: оно не совсем уместно в предложении Runtime Keys, но хорошо подходит здесь, в документации Node.js.

Вышеуказанные определения могут быть перенесены в специальный реестр условий в надлежащее время.

Самообращение к пакету по его имени

[История]

ВерсияИзменения
v13.6.0, v12.16.0Снято ограничение на самообращение к пакету по его имени.
v13.1.0, v12.16.0Добавлено в: v13.1.0, v12.16.0

Внутри пакета значения, определенные в поле package.json пакета "exports", могут быть указаны через имя пакета. Например, если предположить, что package.json выглядит следующим образом:

json
// package.json
{
  "name": "a-package",
  "exports": {
    ".": "./index.mjs",
    "./foo.js": "./foo.js"
  }
}

Тогда любой модуль в этом пакете может ссылаться на экспорт в самом пакете:

js
// ./a-module.mjs
import { something } from 'a-package'; // Импортирует "something" из ./index.mjs.

Самообращение доступно только если package.json имеет "exports", и позволит импортировать только то, что "exports"package.json) разрешает. Таким образом, приведенный ниже код, учитывая предыдущий пакет, вызовет ошибку во время выполнения:

js
// ./another-module.mjs

// Импортирует "another" из ./m.mjs. Ошибка, потому что
// поле "exports" в "package.json"
// не предоставляет экспорт с именем "./m.mjs".
import { another } from 'a-package/m.mjs';

Самообращение также доступно при использовании require, как в ES-модуле, так и в CommonJS. Например, этот код также будет работать:

js
// ./a-module.js
const { something } = require('a-package/foo.js'); // Загружает из ./foo.js.

Наконец, самообращение также работает с пакетами с областью видимости. Например, этот код также будет работать:

json
// package.json
{
  "name": "@my/package",
  "exports": "./index.js"
}
js
// ./index.js
module.exports = 42;
js
// ./other.js
console.log(require('@my/package'));
bash
$ node other.js
42

Двойные пакеты CommonJS/ES module

Подробности смотрите в репозитории примеров пакетов.

Определения полей package.json Node.js

В этом разделе описываются поля, используемые средой выполнения Node.js. Другие инструменты (например, npm) используют дополнительные поля, которые игнорируются Node.js и не документированы здесь.

В Node.js используются следующие поля в файлах package.json:

  • "name" - Актуально при использовании именованных импортов внутри пакета. Также используется менеджерами пакетов в качестве имени пакета.
  • "main" - Модуль по умолчанию при загрузке пакета, если не указан exports, и в версиях Node.js до появления exports.
  • "packageManager" - Менеджер пакетов, рекомендуемый при внесении вклада в пакет. Используется прокладками Corepack.
  • "type" - Тип пакета, определяющий, загружать ли файлы .js как модули CommonJS или ES.
  • "exports" - Экспорт пакетов и условный экспорт. При наличии ограничивает, какие подмодули можно загружать из пакета.
  • "imports" - Импорт пакетов для использования модулями внутри самого пакета.

"name"

[История]

ВерсияИзменения
v13.6.0, v12.16.0Удалена опция --experimental-resolve-self.
v13.1.0, v12.16.0Добавлено в: v13.1.0, v12.16.0
json
{
  "name": "имя-пакета"
}

Поле "name" определяет имя вашего пакета. Публикация в реестре npm требует имени, которое удовлетворяет определенным требованиям.

Поле "name" может быть использовано в дополнение к полю "exports" для самостоятельной ссылки на пакет, используя его имя.

"main"

Добавлено в: v0.4.0

json
{
  "main": "./index.js"
}

Поле "main" определяет точку входа в пакет при импорте по имени через поиск в node_modules. Его значение - это путь.

Когда пакет имеет поле "exports", оно будет иметь приоритет над полем "main" при импорте пакета по имени.

Он также определяет скрипт, который используется, когда каталог пакета загружается через require().

js
// Разрешается в ./path/to/directory/index.js.
require('./path/to/directory');

"packageManager"

Добавлено в: v16.9.0, v14.19.0

[Стабильность: 1 - Экспериментально]

Стабильность: 1 Стабильность: 1 - Экспериментально

json
{
  "packageManager": "<имя менеджера пакетов>@<версия>"
}

Поле "packageManager" определяет, какой менеджер пакетов должен использоваться при работе над текущим проектом. Его можно установить для любого из поддерживаемых менеджеров пакетов и гарантировать, что ваши команды будут использовать точно такие же версии менеджеров пакетов без необходимости устанавливать что-либо, кроме Node.js.

Это поле в настоящее время является экспериментальным и требует включения; подробности о процедуре смотрите на странице Corepack.

"type"

[История]

ВерсияИзменения
v13.2.0, v12.17.0Снят флаг --experimental-modules.
v12.0.0Добавлено в: v12.0.0

Поле "type" определяет формат модуля, который Node.js использует для всех файлов .js, для которых этот файл package.json является ближайшим родительским.

Файлы, заканчивающиеся на .js, загружаются как модули ES, когда ближайший родительский файл package.json содержит поле верхнего уровня "type" со значением "module".

Ближайший родительский package.json определяется как первый package.json, найденный при поиске в текущей папке, родительской папке этой папки и так далее до тех пор, пока не будет достигнута папка node_modules или корневой каталог тома.

json
// package.json
{
  "type": "module"
}
bash
# В той же папке, что и предыдущий package.json {#in-same-folder-as-preceding-packagejson}
node my-app.js # Запускается как модуль ES

Если ближайший родительский package.json не содержит поле "type" или содержит "type": "commonjs", файлы .js обрабатываются как CommonJS. Если достигнут корневой каталог тома и не найден package.json, файлы .js обрабатываются как CommonJS.

Операторы import в файлах .js обрабатываются как модули ES, если ближайший родительский package.json содержит "type": "module".

js
// my-app.js, часть того же примера, что и выше
import './startup.js'; // Загружается как модуль ES из-за package.json

Независимо от значения поля "type", файлы .mjs всегда обрабатываются как модули ES, а файлы .cjs всегда обрабатываются как CommonJS.

"exports"

[История]

ВерсияИзменения
v14.13.0, v12.20.0Добавлена поддержка паттернов "exports".
v13.7.0, v12.17.0Снят флаг условных экспортов.
v13.7.0, v12.16.0Реализован логический порядок условных экспортов.
v13.7.0, v12.16.0Удалена опция --experimental-conditional-exports. В версии 12.16.0 условные экспорты по-прежнему находятся за --experimental-modules.
v13.2.0, v12.16.0Реализованы условные экспорты.
v12.7.0Добавлено в: v12.7.0
json
{
  "exports": "./index.js"
}

Поле "exports" позволяет определять точки входа пакета при импорте по имени, загруженного либо через поиск в node_modules, либо через само-ссылку на собственное имя. Оно поддерживается в Node.js 12+ как альтернатива "main", которая может поддерживать определение экспортов подпутей и условных экспортов, инкапсулируя при этом внутренние неэкспортируемые модули.

Условные экспорты также можно использовать в "exports" для определения различных точек входа пакета для каждой среды, в том числе независимо от того, на пакет ссылаются через require или через import.

Все пути, определенные в "exports", должны быть относительными URL-адресами файлов, начинающимися с ./.

"imports"

Добавлено в версии: v14.6.0, v12.19.0

json
// package.json
{
  "imports": {
    "#dep": {
      "node": "dep-node-native",
      "default": "./dep-polyfill.js"
    }
  },
  "dependencies": {
    "dep-node-native": "^1.0.0"
  }
}

Записи в поле imports должны быть строками, начинающимися с #.

Package imports позволяют сопоставлять с внешними пакетами.

Это поле определяет subpath imports для текущего пакета.