Spec-Zone.ru › Node.js

Модули: ECMAScript-модули

История
Версия Изменения
v21.0.0, v20.10.0, v18.20.0

Добавлена экспериментальная поддержка атрибутов импорта.

v20.0.0, v18.19.0

Обработчики настройки модулей выполняются вне основного потока.

v18.6.0, v16.17.0

Добавлена поддержка цепочки обработчиков настройки модулей.

v17.1.0, v16.14.0

Добавлена экспериментальная поддержка утверждений импорта.

v17.0.0, v16.12.0

Объединены обработчики настройки, удалены getFormat, getSource, transformSource, и getGlobalPreloadCode обработчики, добавлены load и globalPreload обработчики, разрешено возвращать format из resolve или load обработчиков.

v14.8.0

Снятие флага Top-Level Await.

v15.3.0, v14.17.0, v12.22.0

Стабилизация реализации модулей.

v14.13.0, v12.20.0

Поддержка обнаружения именованных экспортов CommonJS.

v14.0.0, v13.14.0, v12.20.0

Удаление предупреждения об экспериментальных модулях.

v13.2.0, v12.17.0

Загрузка ECMAScript-модулей больше не требует флага командной строки.

v12.0.0

Добавлена поддержка ES-модулей с использованием .js расширения файла через package.json "type" поле.

v8.5.0

Добавлена в: v8.5.0

Устойчивость: 2 - Стабильно

Введение

ECMAScript-модули — это официально стандартный формат для упаковки JavaScript-кода для повторного использования. Модули определяются с помощью различных import и export инструкций.

Следующий пример ES-модуля экспортирует функцию:

// addTwo.mjs
function addTwo(num) {
  return num + 2;
}

export { addTwo }; copy

Следующий пример ES-модуля импортирует функцию из addTwo.mjs:

// app.mjs
import { addTwo } from './addTwo.mjs';

// Prints: 6
console.log(addTwo(4)); copy

Node.js полностью поддерживает ECMAScript-модули в их текущем виде и обеспечивает взаимодействие между ними и его исходным форматом модулей, CommonJS.

Включение

Node.js имеет две системы модулей: CommonJS-модули и ECMAScript-модули.

Авторы могут указать Node.js интерпретировать JavaScript как ES-модуль через .mjs расширение файла, package.json "type" поле со значением "module", --input-type флаг со значением "module", или --experimental-default-type флаг со значением "module". Это явные метки, указывающие на то, что код предназначен для выполнения как ES-модуль.

Обратно, авторы могут указать Node.js интерпретировать JavaScript как CommonJS через .cjs расширение файла, package.json "type" поле со значением "commonjs", --input-type флаг со значением "commonjs", или --experimental-default-type флаг со значением "commonjs".

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

Пакеты

Этот раздел был перемещен в Модули: Пакеты.

import Спецификаторы

Терминология

Спецификатор инструкции import — это строка после ключевого слова from, например 'node:path' в import { sep } from 'node:path'. Спецификаторы также используются в инструкциях export from и в качестве аргумента выражения import().

Существует три типа спецификаторов:

  • Относительные спецификаторы, такие как './startup.js' или '../config.mjs'. Они ссылаются на путь, относительный к расположению импортируемого файла. Расширение файла для них всегда необходимо.

  • Базовые спецификаторы, такие как 'some-package' или 'some-package/shuffle'. Они могут ссылаться на главную точку входа пакета по имени пакета или на конкретный модуль функций внутри пакета, префикс которого соответствует имени пакета, как показано в примерах соответственно. Включение расширения файла необходимо только для пакетов без "exports" поля.

  • Абсолютные спецификаторы, такие как 'file:///opt/nodejs/config.js'. Они напрямую и явно ссылаются на полный путь.

Разрешение базовых спецификаторов обрабатывается алгоритмом разрешения и загрузки модулей Node.js. Все остальные разрешения спецификаторов всегда разрешаются только с помощью стандартной семантики разрешения относительных URL.

Как и в CommonJS, файлы модулей внутри пакетов можно получить, добавив путь к имени пакета, за исключением случаев, когда пакет package.json содержит "exports" поле. В этом случае доступ к файлам внутри пакетов возможен только по путям, определённым в "exports".

Дополнительные сведения о правилах разрешения пакетов, которые применяются к базовым спецификаторам в разрешении модулей Node.js, см. в документации по пакетам.

Обязательные расширения файлов

Расширение файла должно быть указано при использовании ключевого слова import для разрешения относительных или абсолютных спецификаторов. Индексы каталогов (например, './startup/index.js') также должны быть полностью указаны.

Это поведение соответствует поведению import в средах браузера, предполагая типично настроенный сервер.

URL

ES-модули разрешаются и кэшируются как URL. Это означает, что специальные символы должны быть кодированы в процентах, такие как # с %23 и ? с %3F.

file:, node:, и data: схемы URL поддерживаются. Спецификатор, такой как 'https://example.com/app.js' не поддерживается напрямую в Node.js, если не используется настраиваемый HTTPS-загрузчик.

file: URL

Модули загружаются несколько раз, если спецификатор import , используемый для их разрешения, имеет другой запрос или фрагмент.

import './foo.mjs?query=1'; // loads ./foo.mjs with query of "?query=1"
import './foo.mjs?query=2'; // loads ./foo.mjs with query of "?query=2" copy

Корень тома может быть указан через /, //, или file:///. Учитывая различия между разрешением URL и путей (например, детали кодирования в процентах), рекомендуется использовать url.pathToFileURL при импорте пути.

data: импорты
Добавлена в: v12.10.0

data: URL поддерживаются для импорта со следующими типами MIME:

  • text/javascript для ES-модулей
  • application/json для JSON
  • application/wasm для Wasm
import 'data:text/javascript,console.log("hello!");';
import _ from 'data:application/json,"world!"' with { type: 'json' }; copy

data: URL разрешают только базовые спецификаторы для встроенных модулей и абсолютные спецификаторы. Разрешение относительных спецификаторов не работает, потому что data: не является специальной схемой. Например, попытка загрузить ./foo из data:text/javascript,import "./foo"; не приводит к разрешению, потому что для data: URL нет понятия относительного разрешения.

node: импорты
История
Версия Изменения
v16.0.0, v14.18.0

Добавлена поддержка импорта node: в require(...).

v14.13.1, v12.20.0

Добавлена в: v14.13.1, v12.20.0

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

import fs from 'node:fs/promises'; copy

Атрибуты импорта

История
Версия Изменения
v21.0.0, v20.10.0, v18.20.0

Переключение с Утверждений импорта на Атрибуты импорта.

v17.1.0, v16.14.0

Добавлена в: v17.1.0, v16.14.0

Устойчивость: 1.1 - Активное развитие

Эта функция ранее называлась «Утверждения импорта», и использовалось ключевое слово assert вместо with. Любые использования в коде предыдущего ключевого слова assert должны быть обновлены на with.

Предложение Атрибуты импорта добавляет встроенный синтаксис для инструкций импорта модулей, чтобы передавать дополнительную информацию вместе со спецификатором модуля.

import fooData from './foo.json' with { type: 'json' };

const { default: barData } =
  await import('./bar.json', { with: { type: 'json' } }); copy

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

Атрибут type Необходим для
'json' JSON-модули

Встроенные модули

Встроенные модули предоставляют именованные экспорты своей публичной API. Также предоставляется экспорт по умолчанию, являющийся значением экспорта CommonJS. Экспорт по умолчанию может использоваться, помимо прочего, для изменения именованных экспортов. Именованные экспорты встроенных модулей обновляются только путем вызова module.syncBuiltinESMExports().

import EventEmitter from 'node:events';
const e = new EventEmitter(); copy
import { readFile } from 'node:fs';
readFile('./foo.txt', (err, source) => {
  if (err) {
    console.error(err);
  } else {
    console.log(source);
  }
}); copy
import fs, { readFileSync } from 'node:fs';
import { syncBuiltinESMExports } from 'node:module';
import { Buffer } from 'node:buffer';

fs.readFileSync = () => Buffer.from('Hello, ESM');
syncBuiltinESMExports();

fs.readFileSync === readFileSync; copy

import() выражения

Динамические import() поддерживаются как в CommonJS, так и в модулях ES. В модулях CommonJS они могут использоваться для загрузки модулей ES.

import.meta

  • <Объект>

Свойство import.meta meta является Object, содержащим следующие свойства.

import.meta.dirname

Добавлен в: v21.2.0, v20.11.0
Устойчивость: 1.2 — кандидат в релиз
  • <строка> Имя директории текущего модуля. Это то же самое, что и path.dirname() для import.meta.filename.

Примечание: присутствует только в модулях file:.

import.meta.filename

Добавлен в: v21.2.0, v20.11.0
Устойчивость: 1.2 — кандидат в релиз
  • <строка> Полный абсолютный путь и имя файла текущего модуля с разрешёнными символическими ссылками.
  • Это то же самое, что и url.fileURLToPath() для import.meta.url.

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

import.meta.url

  • <строка> Абсолютный file: URL модуля.

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

Это позволяет использовать полезные шаблоны, такие как загрузка файлов по относительным путям:

import { readFileSync } from 'node:fs';
const buffer = readFileSync(new URL('./data.proto', import.meta.url)); copy

import.meta.resolve(specifier)

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

Убрана отметка import.meta.resolve, при этом параметр parentURL по-прежнему помечен.

v20.6.0, v18.19.0

Этот API больше не выбрасывает исключения при обработке file: URL-адресов, которые не отображаются в локальной файловой системе.

v20.0.0, v18.19.0

Этот API теперь возвращает строку синхронно, а не промис.

v16.2.0, v14.18.0

Добавлена поддержка объекта WHATWG URL для параметра parentURL.

v13.9.0, v12.16.2

Добавлен в: v13.9.0, v12.16.2

Устойчивость: 1.2 — кандидат в релиз
  • specifier <строка> Спецификатор модуля для разрешения относительно текущего модуля.
  • Возвращает: <строка> Абсолютный URL-адрес, к которому бы разрешился спецификатор.

import.meta.resolve — это функция разрешения, относящаяся к модулю, определенная для каждого модуля, возвращающая строку URL-адреса.

const dependencyAsset = import.meta.resolve('component-lib/asset.css');
// file:///app/node_modules/component-lib/asset.css
import.meta.resolve('./dep.js');
// file:///app/dep.js copy

Поддерживаются все функции разрешения модулей Node.js. Разрешение зависимостей подчиняется разрешениям экспортов, разрешенным в пакете.

Примечания:

  • Это может привести к синхронным операциям с файловой системой, что может повлиять на производительность аналогично require.resolve.
  • Эта функция недоступна внутри пользовательских загрузчиков (это привело бы к тупику).

Нестандартный API:

При использовании флага --experimental-import-meta-resolve, эта функция принимает второй аргумент:

  • parent <строка> | <URL> Необязательный абсолютный URL-адрес родительского модуля для разрешения. По умолчанию: import.meta.url

Совместимость с CommonJS

import операторы

Оператор import может ссылаться на модуль ES или CommonJS. Операторы import разрешены только в модулях ES, но динамические выражения import() поддерживаются в CommonJS для загрузки модулей ES.

При импорте модулей CommonJS объект module.exports предоставляется в качестве экспорта по умолчанию. Именованные экспорты могут быть доступны, предоставляемые статическим анализом для повышения совместимости с экосистемой.

require

Модуль CommonJS require в настоящее время поддерживает загрузку только синхронных модулей ES, когда --experimental-require-module включён.

См. Загрузка модулей ECMAScript с использованием require() для получения подробной информации.

Пространства имён CommonJS

Модули CommonJS состоят из объекта module.exports, который может быть любого типа.

При импорте модуля CommonJS он может быть надёжно импортирован с использованием импорта по умолчанию ES-модулей или соответствующей синтаксической конструкции:

import { default as cjs } from 'cjs';

// The following import statement is "syntax sugar" (equivalent but sweeter)
// for `{ default as cjsSugar }` in the above import statement:
import cjsSugar from 'cjs';

console.log(cjs);
console.log(cjs === cjsSugar);
// Prints:
//   <module.exports>
//   true copy

Представление модуля CommonJS в пространстве имён ECMAScript — это всегда пространство имён со значением экспорта default , указывающим на значение module.exports CommonJS.

Этот модуль namespace Exotic Object можно напрямую наблюдать, используя import * as m from 'cjs' или динамический импорт:

import * as m from 'cjs';
console.log(m);
console.log(m === await import('cjs'));
// Prints:
//   [Module] { default: <module.exports> }
//   true copy

Для лучшей совместимости с существующим использованием в экосистеме JavaScript, Node.js дополнительно пытается определить именованные экспорты модулей CommonJS каждого импортированного модуля CommonJS, чтобы предоставить их в качестве отдельных экспортов модулей ES с помощью процесса статического анализа.

Например, рассмотрим модуль CommonJS, написанный:

// cjs.cjs
exports.name = 'exported'; copy

Предыдущий модуль поддерживает именованные импорты в модулях ES:

import { name } from './cjs.cjs';
console.log(name);
// Prints: 'exported'

import cjs from './cjs.cjs';
console.log(cjs);
// Prints: { name: 'exported' }

import * as m from './cjs.cjs';
console.log(m);
// Prints: [Module] { default: { name: 'exported' }, name: 'exported' } copy

Как видно из последнего примера вывода модуля namespace Exotic Object, экспорт name копируется из объекта module.exports и устанавливается непосредственно в пространстве имён модуля ES при импорте модуля.

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

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

Обнаружение именованных экспортов охватывает множество распространенных шаблонов экспорта, шаблонов переэкспорта и выходы инструментов построения и транспайлеров. Смотрите cjs-module-lexer для точной реализации семантики.

Различия между модулями ES и CommonJS

Отсутствуют require, exports, или module.exports

В большинстве случаев модуль ES import может использоваться для загрузки модулей CommonJS.

При необходимости функцию require можно создать внутри модуля ES, используя module.createRequire().

Отсутствуют __filename или __dirname

Эти переменные CommonJS недоступны в модулях ES.

Случаи использования __filename и __dirname могут быть воспроизведены с помощью import.meta.filename и import.meta.dirname.

Отсутствует загрузка плагинов

Плагины в настоящее время не поддерживаются с импортами модулей ES.

Их можно загрузить с помощью module.createRequire() или process.dlopen.

Отсутствует require.resolve

Относительное разрешение может быть обработано с помощью new URL('./local', import.meta.url).

Для полного require.resolve замены существует API import.meta.resolve.

В качестве альтернативы можно использовать module.createRequire().

Отсутствует NODE_PATH

NODE_PATH не участвует в разрешении import спецификаторов. Пожалуйста, используйте символические ссылки, если это поведение необходимо.

Отсутствуют require.extensions

require.extensions не используется import. Модульные крючки настройки могут предоставить замену.

Отсутствует require.cache

require.cache не используется import , так как у загрузчика модулей ES есть свой отдельный кэш.

Модули JSON

Устойчивость: 1 - Экспериментальная

Файлы JSON могут ссылаться на import:

import packageConfig from './package.json' with { type: 'json' }; copy

Синтаксис with { type: 'json' } обязателен; см. Атрибуты импорта.

Импортированный JSON экспортирует только default экспорт. Именованные экспорты не поддерживаются. В кэше CommonJS создается запись, чтобы избежать дублирования. Тот же объект возвращается в CommonJS, если модуль JSON уже был импортирован из того же пути.

Модули Wasm

Устойчивость: 1 - Экспериментально

Импорт модулей WebAssembly поддерживается под флагом --experimental-wasm-modules, позволяя импортировать любые .wasm файлы как обычные модули, поддерживая также их импорты модулей.

Эта интеграция соответствует Предложению по интеграции модулей ES для WebAssembly.

Например, index.mjs содержащий:

import * as M from './module.wasm';
console.log(M); copy

выполняется под:

node --experimental-wasm-modules index.mjs copy

предоставит интерфейс экспорта для инициализации module.wasm.

Оператор ожидания await в верхнем уровне

Добавлен в: v14.8.0

Ключевое слово await может использоваться в теле верхнего уровня модуля ECMAScript.

Предположим модуль a.mjs с

export const five = await Promise.resolve(5); copy

И модуль b.mjs с

import { five } from './a.mjs';

console.log(five); // Logs `5` copy
node b.mjs # works copy

Если выражение верхнего уровня await никогда не разрешается, процесс node завершится со статусом 13 код завершения.

import { spawn } from 'node:child_process';
import { execPath } from 'node:process';

spawn(execPath, [
  '--input-type=module',
  '--eval',
  // Never-resolving Promise:
  'await new Promise(() => {})',
]).once('exit', (code) => {
  console.log(code); // Logs `13`
}); copy

Импорт HTTPS и HTTP

Устойчивость: 1 - Экспериментально

Импорт модулей, основанных на сети, используя https: и http:, поддерживается под флагом --experimental-network-imports Это позволяет использовать импорты, похожие на веб-браузерные, в Node.js с небольшими отличиями из-за проблем со стабильностью и безопасностью приложения, которые отличаются при работе в привилегированной среде вместо среды браузера.

Импорты ограничены HTTP/1

Автоматическое согласование протокола для HTTP/2 и HTTP/3 пока не поддерживается.

HTTP ограничен адресами обратной петли

http: уязвим к атакам «человек посередине» и не разрешён для использования с адресами за пределами IPv4-адресного 127.0.0.0/8 (127.0.0.1 до 127.255.255.255) и IPv6-адресного ::1. Поддержка http: предназначена для локального развития.

Аутентификация никогда не отправляется на целевой сервер.

Заголовки Authorization, Cookie и Proxy-Authorization не отправляются на сервер. Избегайте включения информации о пользователе в части импортированных URL-адресов. Разрабатывается модель безопасности для безопасного использования этих данных на сервере.

CORS никогда не проверяется на целевом сервере

CORS разработан для того, чтобы сервер мог ограничивать потребителей API определённым набором хостов. Это не поддерживается, так как это не имеет смысла для реализации на основе сервера.

Невозможно загрузить зависимости, не относящиеся к сети

Эти модули не могут обращаться к другим модулям, не находящимся по http: или https:. Чтобы по-прежнему обращаться к локальным модулям, избегая проблем с безопасностью, передайте ссылки на локальные зависимости:

// file.mjs
import worker_threads from 'node:worker_threads';
import { configure, resize } from 'https://example.com/imagelib.mjs';
configure({ worker_threads }); copy
// https://example.com/imagelib.mjs
let worker_threads;
export function configure(opts) {
  worker_threads = opts.worker_threads;
}
export function resize(img, size) {
  // Perform resizing in worker_thread to avoid main thread blocking
} copy

Загрузка по сети не включена по умолчанию

Сейчас для включения загрузки ресурсов по http: или https: нужен флаг --experimental-network-imports. В будущем будет использован другой механизм для обеспечения этого. Требуется явное включение, чтобы предотвратить нежелательное использование транзитивных зависимостей, которые могут повлиять на надёжность приложений Node.js.

Загрузчики

Предыдущая документация по загрузчикам теперь находится по адресу Модули: Настраиваемые хуки.

Алгоритм разрешения и загрузки

Особенности

Решатель по умолчанию имеет следующие свойства:

  • Разрешение на основе FileURL, как используется в модулях ES
  • Разрешение относительных и абсолютных URL
  • Нет расширений по умолчанию
  • Нет главных папок
  • Поиск разрешения пакета с голым спецификатором через node_modules
  • Не приводит к ошибке при неизвестных расширениях или протоколах
  • Может необязательно предоставлять подсказку о формате фазе загрузки

Загрузчик по умолчанию имеет следующие свойства

  • Поддержка загрузки встроенных модулей через URL node:
  • Поддержка загрузки "встроенных" модулей через URL data:
  • Поддержка загрузки модулей file:
  • Возвращает ошибку для любого другого протокола URL
  • Возвращает ошибку для неизвестных расширений при загрузке file: (поддерживаются только .cjs, .js и .mjs)

Алгоритм разрешения

Алгоритм загрузки спецификатора модуля ES задается с помощью метода ESM_RESOLVE ниже. Он возвращает разрешенный URL для спецификатора модуля относительно parentURL.

Алгоритм разрешения определяет полный разрешенный URL для загрузки модуля, а также его предполагаемый формат модуля. Алгоритм разрешения не определяет, может ли быть загружен протокол разрешенного URL, или разрешены ли расширения файлов, вместо этого эти проверки применяются Node.js на этапе загрузки (например, если его попросили загрузить URL, имеющий протокол, который не является file:, data:, node:, или если включен --experimental-network-imports, https:).

Алгоритм также пытается определить формат файла на основе расширения (см. алгоритм ESM_FILE_FORMAT ниже). Если он не распознает расширение файла (например, если это не .mjs, .cjs или .json), то возвращается формат undefined, который вызовет ошибку на этапе загрузки.

Алгоритм определения формата модуля разрешенного URL предоставляется функцией ESM_FILE_FORMAT, которая возвращает уникальный формат модуля для любого файла. Формат "module" возвращается для модуля ECMAScript, а формат "commonjs" используется для обозначения загрузки через устаревший загрузчик CommonJS. Дополнительные форматы, такие как "addon", могут быть расширены в будущих обновлениях.

В следующих алгоритмах все ошибки подпрограмм распространяются как ошибки этих главных процедур, если не указано иное.

defaultConditions — это массив имен условной среды, ["node", "import"].

Решатель может вызывать следующие ошибки:

  • Неверный спецификатор модуля: спецификатор модуля является неверным URL, именем пакета или спецификатором подпути пакета.
  • Неверная конфигурация пакета: конфигурация package.json неверна или содержит неверную конфигурацию.
  • Неверная цель пакета: экспорт или импорт пакета определяют целевой модуль для пакета, который имеет неверный тип или строковое назначение.
  • Путь пакета не экспортируется: экспорт пакета не определяет или не разрешает целевой подпуть в пакете для данного модуля.
  • Импорт пакета не определен: импорт пакета не определяет спецификатор.
  • Модуль не найден: запрашиваемый пакет или модуль не существует.
  • Неподдерживаемый импорт каталога: разрешенный путь соответствует каталогу, который не является поддерживаемой целью для импорта модулей.

Спецификация алгоритма разрешения

ESM_RESOLVE(specifier, parentURL)

  1. Пусть resolved будет undefined.
  2. Если specifier — это допустимый URL, то
    1. Установите resolved в результат анализа и повторной сериализации specifier как URL.
  3. В противном случае, если specifier начинается с "/", "./" или "../", то
    1. Установите resolved в результат разрешения URL specifier относительно parentURL.
  4. В противном случае, если specifier начинается с "#", то
    1. Установите resolved в результат PACKAGE_IMPORTS_RESOLVE(specifier, parentURL, defaultConditions).
  5. В противном случае,
    1. Примечание: specifier теперь является голым спецификатором.
    2. Установите resolved в результат PACKAGE_RESOLVE(specifier, parentURL).
  6. Пусть format будет undefined.
  7. Если resolved — это URL "file:", то
    1. Если resolved содержит какие-либо процентые кодировки "/" или "\" ("%2F" и "%5C" соответственно), то
      1. Выбросьте ошибку Неверный спецификатор модуля.
    2. Если файл по адресу resolved является каталогом, то
      1. Выбросьте ошибку Неподдерживаемый импорт каталога.
    3. Если файл по адресу resolved не существует, то
      1. Выбросьте ошибку Модуль не найден.
    4. Установите resolved в реальный путь resolved, сохраняя те же компоненты строки запроса и фрагмента URL.
    5. Установите format в результат ESM_FILE_FORMAT(resolved).
  8. В противном случае,
    1. Установите format в формат модуля типа контента, связанного с URL resolved.
  9. Верните format и resolved в фазу загрузки

PACKAGE_RESOLVE(packageSpecifier, parentURL)

  1. Пусть packageName будет undefined.
  2. Если packageSpecifier — это пустая строка, то
    1. Выбросьте ошибку Неверный спецификатор модуля.
  3. Если packageSpecifier — это имя встроенного модуля Node.js, то
    1. Верните строку "node:", объединенную с packageSpecifier.
  4. Если packageSpecifier не начинается с "@", то
    1. Установите packageName в подстроку packageSpecifier до первого разделителя "/" или конца строки.
  5. В противном случае,
    1. Если packageSpecifier не содержит разделителя "/", то
      1. Выбросьте ошибку Неверный спецификатор модуля.
    2. Установите packageName в подстроку packageSpecifier до второго разделителя "/" или конца строки.
  6. Если packageName начинается с "." или содержит "\" или "%", то
    1. Выбросьте ошибку Неверный спецификатор модуля.
  7. Пусть packageSubpath будет ".", объединенной с подстрокой packageSpecifier с позиции длины packageName.
  8. Если packageSubpath заканчивается на "/", то
    1. Выбросьте ошибку Неверный спецификатор модуля.
  9. Пусть selfUrl будет результатом PACKAGE_SELF_RESOLVE(packageName, packageSubpath, parentURL).
  10. Если selfUrl не undefined, верните selfUrl.
  11. Пока parentURL не является корнем файловой системы,
    1. Пусть packageURL будет результатом разрешения URL "node_modules/", объединенной с packageSpecifier, относительно parentURL.
    2. Установите parentURL в родительский URL папки parentURL.
    3. Если папка по адресу packageURL не существует, то
      1. Продолжите следующую итерацию цикла.
    4. Пусть pjson будет результатом READ_PACKAGE_JSON(packageURL).
    5. Если pjson не null и pjson.exports не null или undefined, то
      1. Верните результат PACKAGE_EXPORTS_RESOLVE(packageURL, packageSubpath, pjson.exports, defaultConditions).
    6. В противном случае, если packageSubpath равно ".", то
      1. Если pjson.main — это строка, то
        1. Верните результат разрешения URL main в packageURL.
    7. В противном случае,
      1. Верните результат разрешения URL packageSubpath в packageURL.
  12. Выбросьте ошибку Модуль не найден.

PACKAGE_SELF_RESOLVE(packageName, packageSubpath, parentURL)

  1. Пусть packageURL будет результатом LOOKUP_PACKAGE_SCOPE(parentURL).
  2. Если packageURL равно null, то
    1. Верните undefined.
  3. Пусть pjson будет результатом READ_PACKAGE_JSON(packageURL).
  4. Если pjson равно null или если pjson.exports равно null или undefined, то
    1. Верните undefined.
  5. Если pjson.name равно packageName, то
    1. Верните результат PACKAGE_EXPORTS_RESOLVE(packageURL, packageSubpath, pjson.exports, defaultConditions).
  6. В противном случае, верните undefined.

PACKAGE_EXPORTS_RESOLVE(packageURL, subpath, exports, conditions)

  1. Если exports — это объект, имеющий как ключ, начинающийся с ".", так и ключ, не начинающийся с ".", выбросьте ошибку Неверная конфигурация пакета.
  2. Если subpath равно ".", то
    1. Пусть mainExport будет undefined.
    2. Если exports — это строка или массив, или объект, не содержащий ключей, начинающихся с ".", то
      1. Установите mainExport в exports.
    3. В противном случае, если exports — это объект, содержащий свойство ".", то
      1. Установите mainExport в exports["."].
    4. Если mainExport не undefined, то
      1. Пусть resolved будет результатом PACKAGE_TARGET_RESOLVE( packageURL, mainExport, null, false, conditions).
      2. Если resolved не null или undefined, верните resolved.
  3. В противном случае, если exports — это объект и все ключи exports начинаются с ".", то
    1. Утверждение: subpath начинается с "./".
    2. Пусть resolved будет результатом PACKAGE_IMPORTS_EXPORTS_RESOLVE( subpath, exports, packageURL, false, conditions).
    3. Если resolved не null или undefined, верните resolved.
  4. Выбросьте ошибку Путь пакета не экспортируется.

РАЗРЕШЕНИЕ_ИМПОРТОВ_ПАКЕТА(specifier, parentURL, conditions)

  1. Утверждение: specifier начинается с "#".
  2. Если specifier точно равен "#" или начинается с "#/", тогда
    1. Выбросить ошибку Неверный спецификатор модуля.
  3. Пусть packageURL будет результатом ПОИСК_ОБЛАСТИ_ПАКЕТА(parentURL).
  4. Если packageURL не null, тогда
    1. Пусть pjson будет результатом ЧТЕНИЕ_JSON_ПАКЕТА(packageURL).
    2. Если pjson.imports является не-null объектом, тогда
      1. Пусть resolved будет результатом РАЗРЕШЕНИЕ_ИМПОРТОВ_ЭКСПОРТОВ_ПАКЕТА( specifier, pjson.imports, packageURL, true, conditions).
      2. Если resolved не null или undefined, вернуть resolved.
  5. Выбросить ошибку Импорт пакета не определён.

РАЗРЕШЕНИЕ_ИМПОРТОВ_ЭКСПОРТОВ_ПАКЕТА(matchKey, matchObj, packageURL, isImports, conditions)

  1. Если matchKey является ключом matchObj и не содержит "*", тогда
    1. Пусть target будет значением matchObj[matchKey].
    2. Вернуть результат РАЗРЕШЕНИЕ_ЦЕЛИ_ПАКЕТА(packageURL, target, null, isImports, conditions).
  2. Пусть expansionKeys будет списком ключей matchObj, содержащих только один "*", отсортированным по функции сортировки СРАВНЕНИЕ_КЛЮЧА_ШАБЛОНА, которая упорядочивает по убыванию специфичности.
  3. Для каждого ключа expansionKey в expansionKeys выполнить
    1. Пусть patternBase будет подстрокой expansionKey до, но не включая, первого символа "*".
    2. Если matchKey начинается с, но не равно, patternBase, тогда
      1. Пусть patternTrailer будет подстрокой expansionKey с индекса после первого символа "*".
      2. Если patternTrailer имеет нулевую длину, или если matchKey заканчивается на patternTrailer и длина matchKey больше или равна длине expansionKey, тогда
        1. Пусть target будет значением matchObj[expansionKey].
        2. Пусть patternMatch будет подстрокой matchKey, начиная с индекса длины patternBase до длины matchKey минус длина patternTrailer.
        3. Вернуть результат РАЗРЕШЕНИЕ_ЦЕЛИ_ПАКЕТА(packageURL, target, patternMatch, isImports, conditions).
  4. Вернуть null.

СРАВНЕНИЕ_КЛЮЧА_ШАБЛОНА(keyA, keyB)

  1. Утверждение: keyA содержит только один "*".
  2. Утверждение: keyB содержит только один "*".
  3. Пусть baseLengthA будет индексом "*" в keyA.
  4. Пусть baseLengthB будет индексом "*" в keyB.
  5. Если baseLengthA больше baseLengthB, вернуть -1.
  6. Если baseLengthB больше baseLengthA, вернуть 1.
  7. Если длина keyA больше длины keyB, вернуть -1.
  8. Если длина keyB больше длины keyA, вернуть 1.
  9. Вернуть 0.

РАЗРЕШЕНИЕ_ЦЕЛИ_ПАКЕТА(packageURL, target, patternMatch, isImports, conditions)

  1. Если target является строкой, тогда
    1. Если target не начинается с "./", тогда
      1. Если isImports ложно, или если target начинается с "../" или "/", или если target является корректным URL, тогда
        1. Выбросить ошибку Неверная цель пакета.
      2. Если patternMatch является строкой, тогда
        1. Вернуть РАЗРЕШЕНИЕ_ПАКЕТА(target с каждой заменой "*" на patternMatch, packageURL + "/").
      3. Вернуть РАЗРЕШЕНИЕ_ПАКЕТА(target, packageURL + "/").
    2. Если target, разделённый на "/" или "\", содержит какие-либо "", ".", ".." или "node_modules" сегменты после первого сегмента ".", регистронезависимо и включая проценты, выбросить ошибку Неверная цель пакета.
    3. Пусть resolvedTarget будет результатом разрешения URL конкатенации packageURL и target.
    4. Утверждение: packageURL содержится в resolvedTarget.
    5. Если patternMatch равно null, тогда
      1. Вернуть resolvedTarget.
    6. Если patternMatch, разделённый на "/" или "\", содержит какие-либо "", ".", ".." или "node_modules" сегменты, регистронезависимо и включая проценты, выбросить ошибку Неверный спецификатор модуля.
    7. Вернуть результат разрешения URL resolvedTarget с заменой каждой "*" на patternMatch.
  2. Иначе, если target является не-null объектом, тогда
    1. Если target содержит ключи свойств индексов, как определено в ECMA-262 6.1.7 Индекс массива, выбросить ошибку Неверная конфигурация пакета.
    2. Для каждого свойства p объекта target в порядке вставки
      1. Если p равно "default" или conditions содержит запись для p, тогда
        1. Пусть targetValue будет значением свойства p в target.
        2. Пусть resolved будет результатом РАЗРЕШЕНИЕ_ЦЕЛИ_ПАКЕТА( packageURL, targetValue, patternMatch, isImports, conditions).
        3. Если resolved равно undefined, продолжить цикл.
        4. Вернуть resolved.
    3. Вернуть undefined.
  3. Иначе, если target является массивом, тогда
    1. Если target.length равно нулю, вернуть null.
    2. Для каждого элемента targetValue в target выполнить
      1. Пусть resolved будет результатом РАЗРЕШЕНИЕ_ЦЕЛИ_ПАКЕТА( packageURL, targetValue, patternMatch, isImports, conditions), продолжая цикл при любой ошибке Неверная цель пакета.
      2. Если resolved равно undefined, продолжить цикл.
      3. Вернуть resolved.
    3. Вернуть или выбросить последнее решение по умолчанию null или ошибку.
  4. Иначе, если target равно null, вернуть null.
  5. Иначе выбросить ошибку Неверная цель пакета.

ФОРМАТ_ФАЙЛА_ESM(url)

  1. Утверждение: url соответствует существующему файлу.
  2. Если url заканчивается на ".mjs", тогда
    1. Вернуть "module".
  3. Если url заканчивается на ".cjs", тогда
    1. Вернуть "commonjs".
  4. Если url заканчивается на ".json", тогда
    1. Вернуть "json".
  5. Если --experimental-wasm-modules включено и url заканчивается на ".wasm", тогда
    1. Вернуть "wasm".
  6. Пусть packageURL будет результатом ПОИСК_ОБЛАСТИ_ПАКЕТА(url).
  7. Пусть pjson будет результатом ЧТЕНИЕ_JSON_ПАКЕТА(packageURL).
  8. Пусть packageType будет null.
  9. Если pjson?.type равно "module" или "commonjs", тогда
    1. Установить packageType в pjson.type.
  10. Если url заканчивается на ".js", тогда
    1. Если packageType не null, тогда
      1. Вернуть packageType.
    2. Если --experimental-detect-module включено и результат ОБНАРУЖИТЬ_СИНТАКСИС_МОДУЛЯ(source) равен true, тогда
      1. Вернуть "module".
    3. Вернуть "commonjs".
  11. Если url не имеет расширения, тогда
    1. Если packageType равно "module" и --experimental-wasm-modules включено и файл по адресу url содержит заголовок модуля WebAssembly, тогда
      1. Вернуть "wasm".
    2. Если packageType не null, тогда
      1. Вернуть packageType.
    3. Если --experimental-detect-module включено и исходный код модуля содержит статический импорт или экспорт, тогда
      1. Вернуть "module".
    4. Вернуть "commonjs".
  12. Вернуть undefined (вызовет ошибку на этапе загрузки).

ПОИСК_ОБЛАСТИ_ПАКЕТА(url)

  1. Пусть scopeURL будет url.
  2. Пока scopeURL не является корнем файловой системы,
    1. Установить scopeURL на родительский URL scopeURL.
    2. Если scopeURL заканчивается на фрагменте пути "node_modules", вернуть null.
    3. Пусть pjsonURL будет результатом разрешения "package.json" внутри scopeURL.
    4. Если файл по адресу pjsonURL существует, тогда
      1. Вернуть scopeURL.
  3. Вернуть null.

ЧТЕНИЕ_JSON_ПАКЕТА(packageURL)

  1. Пусть pjsonURL будет результатом разрешения "package.json" внутри packageURL.
  2. Если файл по адресу pjsonURL не существует, тогда
    1. Вернуть null.
  3. Если файл по адресу packageURL не может быть распарсен как корректный JSON, тогда
    1. Выбросить ошибку Неверная конфигурация пакета.
  4. Вернуть парсинговый результат JSON источника файла по адресу pjsonURL.

ОБНАРУЖИТЬ_СИНТАКСИС_МОДУЛЯ(source)

  1. Разбор source как ECMAScript модуля.
  2. Если разбор успешен, тогда
    1. Если source содержит верхнеуровневые await, статические import или export инструкции, или import.meta, вернуть true.
    2. Если source содержит верхнеуровневую лексическую декларацию (const, let, или class) любой из переменных обертки CommonJS (require, exports, module, __filename, или __dirname), вернуть true.
  3. Иначе вернуть false.

Настройка алгоритма разрешения спецификаторов ESM

Настройка хуков модулей обеспечивают механизм настройки алгоритма разрешения спецификаторов ESM. Пример, предоставляющий разрешение в стиле CommonJS для спецификаторов ESM, — commonjs-extension-resolution-loader.

© 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/api/esm.html

Spec-Zone.ru

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