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

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

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

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

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

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

Поле "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-модули независимо от ближайшего родительского 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-модуль, назвав его с расширением .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 не указан.

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

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

Стабильно: 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 для любого абсолютного подпути пакета, такого как 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-файлами.

Свойство экспортов, являющихся статически перечисляемыми, сохраняется с шаблонами экспорта, поскольку отдельные экспорты для пакета могут быть определены путем обработки целевого шаблона правой стороны как glob ** для списка файлов в пакете. Поскольку пути 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-модулями, которые не содержат top-level await в своем графе модулей — в противном случае, при вызове require() модуля будет выброшено исключение ERR_REQUIRE_ASYNC_MODULE.
  • "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"
  }
}

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

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

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

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

bash
node --conditions=development index.js

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

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

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

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

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

Строки условий, отличные от условий "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-модули

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

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

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

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

  • "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": "package-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": "<package manager name>@<version>"
}

Поле "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 должны быть строками, начинающимися с #.

Импорт пакетов позволяет сопоставлять внешние пакеты.

Это поле определяет импорт подпутей для текущего пакета.