Spec-Zone.ru › Node.js 22 LTS

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

История
Версия Изменения
v22.12.0

Атрибуты импорта больше не являются экспериментальными.

v22.0.0

Удалена поддержка утверждений импорта.

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

Флаг для 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'. Они напрямую и явно указывают полный путь.

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

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

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

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

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

Такое поведение соответствует работе import в браузерах при условии типичной конфигурации сервера.

URL

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

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

URL file:

Модули загружаются несколько раз, если спецификатор 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

URL data: поддерживаются для импорта со следующими 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

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

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

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

v14.13.1, v12.20.0

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

URL node: поддерживаются как альтернативный способ загрузки встроенных модулей 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

Атрибуты импорта — это встроенный синтаксис для инструкций импорта модулей, позволяющий передавать дополнительную информацию вместе со спецификатором модуля.

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

Атрибут 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

  • Тип: <Object>

Мета-свойство import.meta — это Object, содержащее следующие свойства. Оно поддерживается только в модулях ES.

import.meta.dirname

История
Версия Изменения
v22.16.0

Это свойство больше не является экспериментальным.

v21.2.0, v20.11.0

Добавлено в: v21.2.0, v20.11.0

  • Тип: <string> Имя каталога текущего модуля.

Это то же самое, что и path.dirname() для import.meta.filename.

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

import.meta.filename

История
Версия Изменения
v22.16.0

Это свойство больше не является экспериментальным.

v21.2.0, v20.11.0

Добавлено в: v21.2.0, v20.11.0

  • Тип: <string> Полный абсолютный путь и имя файла текущего модуля с разрешёнными символическими ссылками.

Это то же самое, что и url.fileURLToPath() для import.meta.url.

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

import.meta.url

  • Тип: <string> Абсолютный URL file: модуля.

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

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

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

import.meta.main

Добавлено в: v22.18.0
Стабильность: 1.0 - Ранняя разработка
  • Тип: <boolean> true, если текущий модуль является точкой входа текущего процесса; в противном случае — false.

Эквивалентно require.main === module в CommonJS.

Аналогично __name__ == "__main__" в Python.

export function foo() {
  return 'Hello, world';
}

function main() {
  const message = foo();
  console.log(message);
}

if (import.meta.main) main();
// `foo` can be imported from another module without possible side-effects from `main` copy

import.meta.resolve(specifier)

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

Больше не требует флага CLI --experimental-import-meta-resolve, за исключением нестандартного параметра parentURL.

v20.6.0, v18.19.0

Этот API больше не вызывает исключение при указании URL file:, не соответствующих существующему файлу в локальной файловой системе.

v20.0.0, v18.19.0

Теперь этот API синхронно возвращает строку вместо Promise.

v16.2.0, v14.18.0

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

v13.9.0, v12.16.2

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

Стабильность: 1.2 - Кандидат на выпуск
  • specifier <string> Спецификатор модуля, который нужно разрешить относительно текущего модуля.
  • Возвращает: <string> Абсолютная строка 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 <string> | <URL> Необязательный абсолютный URL родительского модуля, относительно которого выполняется разрешение. По умолчанию: import.meta.url

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

Инструкции import

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

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

require

В настоящее время модуль CommonJS require поддерживает загрузку только синхронных модулей ES (то есть модулей ES, в которых не используется await верхнего уровня).

Подробнее см. в разделе Загрузка модулей 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.

Этот экзотический объект пространства имён модуля можно непосредственно наблюдать при использовании 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

Для лучшей совместимости с существующим использованием в экосистеме JS 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

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

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

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

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

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

Нет require, exports или module.exports

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

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

Нет __filename или __dirname

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

Для сценариев использования __filename и __dirname можно применять import.meta.filename и import.meta.dirname.

Нет загрузки дополнений

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

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

Нет require.main

Для замены require.main === module предусмотрен API import.meta.main.

Нет 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

История
Версия Изменения
v22.12.0

Модули JSON больше не являются экспериментальными.

На файлы JSON можно ссылаться с помощью import:

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

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

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

Модули Wasm

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

Для модулей Wasm больше не требуется флаг --experimental-wasm-modules.

Поддерживается импорт как экземпляров модулей WebAssembly, так и импорт на этапе исходного кода WebAssembly.

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

Модули Wasm

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

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

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

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

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

при выполнении с помощью:

node index.mjs copy

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

Встроенные строковые средства JavaScript

Добавлено в: v22.19.0

При импорте модулей WebAssembly предложение WebAssembly JS String Builtins автоматически включается благодаря интеграции ESM. Это позволяет модулям WebAssembly напрямую использовать эффективные встроенные строковые средства, доступные во время компиляции, из пространства имён wasm:js-string.

Например, следующий модуль Wasm экспортирует строковую функцию getLength с помощью встроенного средства wasm:js-string length:

(module
  ;; Compile-time import of the string length builtin.
  (import "wasm:js-string" "length" (func $string_length (param externref) (result i32)))

  ;; Define getLength, taking a JS value parameter assumed to be a string,
  ;; calling string length on it and returning the result.
  (func $getLength (param $str externref) (result i32)
    local.get $str
    call $string_length
  )

  ;; Export the getLength function.
  (export "getLength" (func $get_length))
) copy
import { getLength } from './string-len.wasm';
getLength('foo'); // Returns 3. copy

Встроенные средства Wasm — это импорты времени компиляции, которые связываются при компиляции модуля, а не при создании экземпляра. Они ведут себя не как обычные импорты графа модулей; их нельзя проверить с помощью WebAssembly.Module.imports(mod) или виртуализировать без повторной компиляции модуля с использованием прямого API WebAssembly.compile при отключённых встроенных строковых средствах.

Зарезервированные пространства имён Wasm

Добавлено в: v22.19.0

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

  • wasm-js: — зарезервирован во всех именах импортируемых модулей, именах модулей и именах экспортов.
  • wasm: — зарезервирован в именах импортов и экспортов (имена импортируемых модулей разрешены для поддержки будущих встроенных полифилов).

При импорте модуля с указанными выше зарезервированными именами будет вызвано исключение WebAssembly.LinkError.

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

Загрузчики

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

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

Особенности

Резолвер по умолчанию обладает следующими свойствами:

  • Разрешение на основе 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:).

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

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

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

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

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

  • Invalid Module Specifier: спецификатор модуля является недопустимым URL, именем пакета или спецификатором подпути пакета.
  • Invalid Package Configuration: конфигурация package.json недопустима или содержит недопустимую конфигурацию.
  • Invalid Package Target: экспорты или импорты пакета определяют целевой модуль пакета с недопустимым типом или строковым значением.
  • Package Path Not Exported: экспорты пакета не определяют или не разрешают целевой подпуть в пакете для указанного модуля.
  • Package Import Not Defined: импорты пакета не определяют спецификатор.
  • Module Not Found: запрошенный пакет или модуль не существует.
  • Unsupported Directory Import: разрешенный путь соответствует каталогу, который не является допустимой целью для импорта модулей.

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

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. Выдать ошибку Invalid Module Specifier.
    2. Если файл по адресу resolved является каталогом, то
      1. Выдать ошибку Unsupported Directory Import.
    3. Если файл по адресу resolved не существует, то
      1. Выдать ошибку Module Not Found.
    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. Выдать ошибку Invalid Module Specifier.
  3. Если packageSpecifier является именем встроенного модуля Node.js, то
    1. Вернуть строку "node:", объединенную с packageSpecifier.
  4. Если packageSpecifier не начинается с "@", то
    1. Присвоить packageName подстроку packageSpecifier до первого разделителя "/" или до конца строки.
  5. В противном случае
    1. Если packageSpecifier не содержит разделитель "/", то
      1. Выдать ошибку Invalid Module Specifier.
    2. Присвоить packageName подстроку packageSpecifier до второго разделителя "/" или до конца строки.
  6. Если packageName начинается с "." либо содержит "\" или "%", то
    1. Выдать ошибку Invalid Module Specifier.
  7. Пусть packageSubpath — это ".", объединенная с подстрокой packageSpecifier, начиная с позиции, равной длине packageName.
  8. Пусть selfUrl — результат вызова PACKAGE_SELF_RESOLVE(packageName, packageSubpath, parentURL).
  9. Если selfUrl не равен undefined, вернуть selfUrl.
  10. Пока parentURL не является корнем файловой системы,
    1. Пусть packageURL — результат разрешения URL "node_modules/", объединенного с packageName, относительно 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.
  11. Выдать ошибку Module Not Found.

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)

Примечание: эта функция вызывается непосредственно алгоритмом разрешения CommonJS.

  1. Если exports является объектом, содержащим ключ, начинающийся с ".", и ключ, не начинающийся с ".", выдать ошибку Invalid Package Configuration.
  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. Выдать ошибку Package Path Not Exported.

PACKAGE_IMPORTS_RESOLVE(specifier, parentURL, conditions)

Примечание: эта функция вызывается непосредственно алгоритмом разрешения CommonJS.

  1. Утверждение: specifier начинается с "#".
  2. Если specifier в точности равен "#" или начинается с "#/", то
    1. Выдать ошибку Invalid Module Specifier.
  3. Пусть packageURL — результат вызова LOOKUP_PACKAGE_SCOPE(parentURL).
  4. Если packageURL не равен null, то
    1. Пусть pjson — результат вызова READ_PACKAGE_JSON(packageURL).
    2. Если pjson.imports является ненулевым объектом, то
      1. Пусть resolved — результат вызова PACKAGE_IMPORTS_EXPORTS_RESOLVE( specifier, pjson.imports, packageURL, true, conditions).
      2. Если resolved не равен null или undefined, вернуть resolved.
  5. Выдать ошибку Package Import Not Defined.

PACKAGE_IMPORTS_EXPORTS_RESOLVE(matchKey, matchObj, packageURL, isImports, conditions)

  1. Если matchKey оканчивается на "/", то
    1. Выдать ошибку Invalid Module Specifier.
  2. Если matchKey является ключом matchObj и не содержит "*", то
    1. Пусть target — значение matchObj[matchKey].
    2. Вернуть результат вызова PACKAGE_TARGET_RESOLVE(packageURL, target, null, isImports, conditions).
  3. Пусть expansionKeys — список ключей matchObj, содержащих ровно один символ "*", отсортированный с помощью функции сортировки PATTERN_KEY_COMPARE, которая упорядочивает ключи по убыванию специфичности.
  4. Для каждого ключа 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. Вернуть результат вызова PACKAGE_TARGET_RESOLVE(packageURL, target, patternMatch, isImports, conditions).
  5. Вернуть null.

PATTERN_KEY_COMPARE(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.

PACKAGE_TARGET_RESOLVE(packageURL, target, patternMatch, isImports, conditions)

  1. Если target является строкой, то
    1. Если target не начинается с "./", то
      1. Если isImports равен false или если target начинается с "../" или "/", либо если target является допустимым URL, то
        1. Выдать ошибку Invalid Package Target.
      2. Если patternMatch является строкой, то
        1. Вернуть результат вызова PACKAGE_RESOLVE(target, заменив каждый экземпляр "*" на patternMatch, packageURL + "/").
      3. Вернуть результат вызова PACKAGE_RESOLVE(target, packageURL + "/").
    2. Если при разделении target по "/" или "\" после первого сегмента "." обнаруживаются сегменты "", ".", ".." или "node_modules" (без учета регистра и включая варианты с процентным кодированием), выдать ошибку Invalid Package Target.
    3. Пусть resolvedTarget — результат разрешения URL для конкатенации packageURL и target.
    4. Утверждение: packageURL содержится в resolvedTarget.
    5. Если patternMatch равен null, то
      1. Вернуть resolvedTarget.
    6. Если при разделении patternMatch по "/" или "\" обнаруживаются сегменты "", ".", ".." или "node_modules" (без учета регистра и включая варианты с процентным кодированием), выдать ошибку Invalid Module Specifier.
    7. Вернуть результат разрешения URL для resolvedTarget, заменив каждый экземпляр "*" на patternMatch.
  2. Иначе, если target является ненулевым объектом, то
    1. Если target содержит ключи свойств с индексами, как определено в ECMA-262 6.1.7 Индекс массива, выдать ошибку Invalid Package Configuration.
    2. Для каждого свойства p объекта target в порядке вставки свойств
      1. Если p равен "default" или conditions содержит запись для p, то
        1. Пусть targetValue — значение свойства p в target.
        2. Пусть resolved — результат вызова PACKAGE_TARGET_RESOLVE( packageURL, targetValue, patternMatch, isImports, conditions).
        3. Если resolved равен undefined, продолжить цикл.
        4. Вернуть resolved.
    3. Вернуть undefined.
  3. Иначе, если target является массивом, то
    1. Если _target.length равен нулю, вернуть null.
    2. Для каждого элемента targetValue в target выполнить
      1. Пусть resolved — результат вызова PACKAGE_TARGET_RESOLVE( packageURL, targetValue, patternMatch, isImports, conditions); при ошибке Invalid Package Target продолжить цикл.
      2. Если resolved равен undefined, продолжить цикл.
      3. Вернуть resolved.
    3. Вернуть или выдать ошибку последнего резервного результата разрешения — возврата null или ошибки.
  4. Иначе, если target равен null, вернуть null.
  5. В противном случае выдать ошибку Invalid Package Target.

ESM_FILE_FORMAT(url)

  1. Утверждение: url соответствует существующему файлу.
  2. Если url оканчивается на ".mjs", то
    1. Вернуть "module".
  3. Если url оканчивается на ".cjs", то
    1. Вернуть "commonjs".
  4. Если url оканчивается на ".json", то
    1. Вернуть "json".
  5. Если url оканчивается на ".wasm", то
    1. Вернуть "wasm".
  6. Если включен --experimental-addon-modules и url оканчивается на ".node", то
    1. Вернуть "addon".
  7. Пусть packageURL — результат вызова LOOKUP_PACKAGE_SCOPE(url).
  8. Пусть pjson — результат вызова READ_PACKAGE_JSON(packageURL).
  9. Пусть packageType имеет значение null.
  10. Если pjson?.type равен "module" или "commonjs", то
    1. Присвоить packageType значение pjson.type.
  11. Если url оканчивается на ".js", то
    1. Если packageType не равен null, то
      1. Вернуть packageType.
    2. Если результат вызова DETECT_MODULE_SYNTAX(source) равен true, то
      1. Вернуть "module".
    3. Вернуть "commonjs".
  12. Если у url нет расширения, то
    1. Если packageType равен "module" и файл по адресу url содержит заголовок модуля WebAssembly, то
      1. Вернуть "wasm".
    2. Если packageType не равен null, то
      1. Вернуть packageType.
    3. Если результат вызова DETECT_MODULE_SYNTAX(source) равен true, то
      1. Вернуть "module".
    4. Вернуть "commonjs".
  13. Вернуть undefined (приведет к ошибке на этапе загрузки).

LOOKUP_PACKAGE_SCOPE(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.

READ_PACKAGE_JSON(packageURL)

  1. Пусть pjsonURL — результат разрешения "package.json" внутри packageURL.
  2. Если файл по адресу pjsonURL не существует, то
    1. Вернуть null.
  3. Если файл по адресу packageURL не разбирается как корректный JSON, то
    1. Выдать ошибку Invalid Package Configuration.
  4. Вернуть разобранный исходный JSON файла по адресу pjsonURL.

DETECT_MODULE_SYNTAX(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. Пример, реализующий разрешение спецификаторов ESM в стиле CommonJS, — 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/dist/latest-v22.x/docs/api/esm.html

Spec-Zone.ru

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