Spec-Zone.ru › Node.js 20 LTS

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

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

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

v20.10.0

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

v20.0.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 в их текущей спецификации и обеспечивает взаимодействие между ними и исходным форматом модулей Node.js — 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.

Поддерживаются схемы URL file:, node: и data:. Спецификатор вида '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

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

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

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

Утверждения импорта заменены атрибутами импорта.

v17.1.0, v16.14.0

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

Стабильность: 2 - Стабильный

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

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

При импорте модулей JSON атрибут type: '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

Добавлено в: v20.11.0
Стабильность: 1.2 - Кандидат на выпуск
  • <string> Имя каталога текущего модуля. Оно совпадает с path.dirname() для import.meta.filename.

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

import.meta.filename

Добавлено в: v20.11.0
Стабильность: 1.2 - Кандидат на выпуск
  • <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.resolve(specifier)

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

Флаг с parentURL снят с import.meta.resolve; параметр по-прежнему требует флага.

v20.6.0

Теперь этот API не вызывает исключение при указании URL-адресов file:, которые не соответствуют существующему файлу в локальной файловой системе.

v20.0.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.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

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

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

Стабильность: 2 - Стабильный

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

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

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

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

Модули 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 верхнего уровня 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, который возвращает уникальный формат модуля для любого файла. Для модуля ECMAScript возвращается формат "module", а формат "commonjs" указывает на загрузку через устаревший загрузчик CommonJS. В будущих обновлениях могут быть добавлены дополнительные форматы, например "addon".

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

defaultConditions — это массив имён условного окружения, ["node", "import"].

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

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

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

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 является объектом и все его ключи начинаются с ".", то
    1. Утверждение: subpath начинается с "./".
    2. Пусть resolved будет результатом вызова PACKAGE_IMPORTS_EXPORTS_RESOLVE( subpath, exports, packageURL, false, conditions).
    3. Если resolved не равен null или undefined, вернуть resolved.
  4. Выдать ошибку Путь пакета не экспортирован.

PACKAGE_IMPORTS_RESOLVE(specifier, parentURL, conditions)

  1. Утверждение: specifier начинается с "#".
  2. Если specifier в точности равен "#" или начинается с "#/", то
    1. Выдать ошибку Недопустимый спецификатор модуля.
  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_IMPORTS_EXPORTS_RESOLVE(matchKey, matchObj, packageURL, isImports, conditions)

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

ESM_FILE_FORMAT(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 будет результатом вызова LOOKUP_PACKAGE_SCOPE(url).
  7. Пусть pjson будет результатом вызова READ_PACKAGE_JSON(packageURL).
  8. Пусть packageType имеет значение null.
  9. Если pjson?.type равно "module" или "commonjs", то
    1. Задать packageType значение pjson.type.
  10. Если url заканчивается на ".js", то
    1. Если packageType не равен null, то
      1. Вернуть packageType.
    2. Если результат вызова DETECT_MODULE_SYNTAX(source) равен true, то
      1. Вернуть "module".
    3. Вернуть "commonjs".
  11. Если у url нет расширения, то
    1. Если packageType равен "module", включён --experimental-wasm-modules и файл по адресу url содержит заголовок модуля WebAssembly, то
      1. Вернуть "wasm".
    2. Если packageType не равен null, то
      1. Вернуть packageType.
    3. Если результат вызова DETECT_MODULE_SYNTAX(source) равен true, то
      1. Вернуть "module".
    4. Вернуть "commonjs".
  12. Вернуть 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. Выдать ошибку Недопустимая конфигурация пакета.
  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-v20.x/docs/api/esm.html

Spec-Zone.ru

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