Spec-Zone.ru › Node.js 20 LTS

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

История
Версия Изменения
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 через поле package.json "type".

Введение

Пакет — это дерево каталогов, описанное файлом 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, поля package.json "type" со значениями "module" или "commonjs" либо флаги --input-type или --experimental-default-type. Динамические выражения import() поддерживаются как в CommonJS, так и в ES-модулях и сами по себе не приводят к тому, что файл рассматривается как ES-модуль. См. раздел Определение синтаксиса.

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

  • Файлы с расширением .cjs.

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

  • Строки, переданные в качестве аргумента --eval или --print либо переданные в node через STDIN с флагом --input-type=commonjs.

Помимо этих явных случаев, в ряде ситуаций Node.js выбирает систему модулей по умолчанию в зависимости от значения флага --experimental-default-type:

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

  • Файлы, заканчивающиеся на .js или не имеющие расширения, если в ближайшем родительском файле package.json отсутствует поле "type", за исключением случаев, когда каталог находится внутри каталога node_modules. (Области действия пакетов внутри node_modules всегда рассматриваются как CommonJS, если в файле package.json отсутствует поле "type", вне зависимости от --experimental-default-type, для обеспечения обратной совместимости.)

  • Строки, переданные в качестве аргумента --eval или переданные в node через STDIN, если --input-type не указан.

В настоящее время для этого флага по умолчанию установлено значение "commonjs", но в будущем оно может измениться на "module". Поэтому по возможности лучше указывать значение явно; в частности, авторам пакетов следует всегда добавлять поле "type" в файлы package.json, даже если все исходные файлы пакета используют CommonJS. Явное указание type пакета защитит его от возможного изменения типа по умолчанию в Node.js в будущем, а также упростит инструментам сборки и загрузчикам определение того, как следует интерпретировать файлы пакета.

Определение синтаксиса

История
Версия Изменения
v20.19.0

Определение синтаксиса включено по умолчанию.

v21.1.0, v20.10.0

Добавлено в версии: v21.1.0, v20.10.0

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

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

К неоднозначным входным данным относятся:

  • Файлы с расширением .js или без расширения, если отсутствует управляющий файл package.json либо в нём нет поля type, и при этом не указано --experimental-default-type.
  • Строковые входные данные (--eval или STDIN), если не указаны ни --input-type, ни --experimental-default-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 для определения именованных экспортов, которые доступны, если их удаётся определить с помощью статического анализа. URL импортированных модулей CommonJS преобразуются в абсолютные пути, после чего модули загружаются через загрузчик модулей CommonJS.

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

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

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

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

// my-app.js, treated as an ES module because there is a package.json
// file in the same folder with "type": "module".

import './startup/init.js';
// Loaded as ES module since ./startup contains no package.json file,
// and therefore inherits the "type" value from one level up.

import 'commonjs-package';
// Loaded as CommonJS since ./node_modules/commonjs-package/package.json
// lacks a "type" field or contains "type": "commonjs".

import './node_modules/commonjs-package/index.js';
// Loaded as CommonJS since ./node_modules/commonjs-package/package.json
// lacks a "type" field or contains "type": "commonjs". copy

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

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

import './legacy-file.cjs';
// Loaded as CommonJS since .cjs is always loaded as CommonJS.

import 'commonjs-package/src/index.mjs';
// Loaded as ES module since .mjs is always loaded as ES module. copy

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

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

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

Флаг --input-type

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

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

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 copy

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

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

Стабильность: 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", — в поддерживаемых версиях Node.js приоритет имеет поле "exports", а не "main".

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

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

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

{
  "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"
  }
} copy

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

{
  "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"
  }
} copy

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

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

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

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

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

Если определено поле "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", указав в обоих полях один и тот же модуль:

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

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

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

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

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

Теперь пользователь может импортировать только подстроку пути, определённую в "exports":

import submodule from 'es-module-package/submodule.js';
// Loads ./node_modules/es-module-package/src/submodule.js copy

При попытке импортировать другие подстроки путей возникнет ошибка:

import submodule from 'es-module-package/private-module.js';
// Throws ERR_PACKAGE_PATH_NOT_EXPORTED copy
Расширения в подстроках путей

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

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

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

Сокращённая запись exports

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

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

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

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

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

Импорты подстрок путей

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

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

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

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

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

В этом случае 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 и проблемам с обслуживанием.

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

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

Сопоставление * позволяет получить доступ к вложенным подстрокам путей, поскольку оно лишь заменяет строки.

Все вхождения * в правой части затем заменяются этим значением, в том числе если оно содержит разделители /.

import featureX from 'es-module-package/features/x.js';
// Loads ./node_modules/es-module-package/src/features/x.js

import featureY from 'es-module-package/features/y/y.js';
// Loads ./node_modules/es-module-package/src/features/y/y.js

import internalZ from '#internal/z.js';
// Loads ./node_modules/es-module-package/src/internal/z.js copy

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

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

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

// ./node_modules/es-module-package/package.json
{
  "exports": {
    "./features/*.js": "./src/features/*.js",
    "./features/private-internal/*": null
  }
} copy
import featureInternal from 'es-module-package/features/private-internal/m.js';
// Throws: ERR_PACKAGE_PATH_NOT_EXPORTED

import featureX from 'es-module-package/features/x.js';
// Loads ./node_modules/es-module-package/src/features/x.js copy

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

История
Версия Изменения
v13.7.0, v12.16.0

Условные экспорты больше не требуют флага.

v13.2.0, v12.16.0

Добавлено в: v13.2.0, v12.16.0

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

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

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

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 верхнего уровня. В противном случае при require() модуля будет вызвана ошибка ERR_REQUIRE_ASYNC_MODULE.
  • "default" — универсальное резервное условие, которое соответствует всегда. Это может быть файл CommonJS или модуль ES. Это условие всегда должно идти последним.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

node --conditions=development index.js copy

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

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

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

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

  1. Условие должно содержать хотя бы один символ.
  2. Условие не может начинаться с ".", поскольку оно может встречаться в местах, где также допустимы относительные пути.
  3. Условие не может содержать ",", поскольку некоторые инструменты командной строки могут интерпретировать его как разделитель списка.
  4. Условие не может быть целочисленным ключом свойства, например "10", поскольку это может неожиданно повлиять на порядок ключей свойств объектов 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 имеет следующий вид:

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

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

// ./a-module.mjs
import { something } from 'a-package'; // Imports "something" from ./index.mjs. copy

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

// ./another-module.mjs

// Imports "another" from ./m.mjs. Fails because
// the "package.json" "exports" field
// does not provide an export named "./m.mjs".
import { another } from 'a-package/m.mjs'; copy

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

// ./a-module.js
const { something } = require('a-package/foo.js'); // Loads from ./foo.js. copy

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

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

Пакеты с модулями CommonJS и ES

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

Определения полей 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

  • Тип: <string>
{
  "name": "package-name"
} copy

Поле "name" задаёт имя пакета. Для публикации в реестре npm имя должно соответствовать определённым требованиям.

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

"main"

Добавлено в: v0.4.0
  • Тип: <string>
{
  "main": "./index.js"
} copy

Поле "main" задаёт точку входа пакета при его импорте по имени с помощью поиска node_modules. Его значение — путь.

Если в пакете есть поле "exports", при импорте пакета по имени оно имеет приоритет над полем "main".

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

// This resolves to ./path/to/directory/index.js.
require('./path/to/directory'); copy

"packageManager"

Добавлено в: v16.9.0, v14.19.0
Стабильность: 1 — Экспериментальный
  • Тип: <string>
{
  "packageManager": "<package manager name>@<version>"
} copy

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

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

"type"

История
Версия Изменения
v13.2.0, v12.17.0

Флаг для --experimental-modules удалён.

v12.0.0

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

  • Тип: <string>

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

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

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

// package.json
{
  "type": "module"
} copy
# In same folder as preceding package.json
node my-app.js # Runs as ES module copy

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

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

// my-app.js, part of the same example as above
import './startup.js'; // Loaded as ES module because of package.json copy

Независимо от значения поля "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

  • Тип: <Object> | <string> | <string[]>
{
  "exports": "./index.js"
} copy

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

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

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

"imports"

Добавлено в: v14.6.0, v12.19.0
  • Тип: <Object>
// package.json
{
  "imports": {
    "#dep": {
      "node": "dep-node-native",
      "default": "./dep-polyfill.js"
    }
  },
  "dependencies": {
    "dep-node-native": "^1.0.0"
  }
} copy

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

Импорты пакета допускают сопоставление с внешними пакетами.

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

© Joyent, Inc. and other Node contributors
Licensed under the MIT License.
Node.js is a trademark of Joyent, Inc. and is used with its permission.
We are not endorsed by or affiliated with Joyent.
https://nodejs.org/dist/latest-v20.x/docs/api/packages.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API