Skip to content

Модули: API node:module

Добавлен в: v0.3.7

Объект Module

Предоставляет общие служебные методы при взаимодействии с экземплярами Module, переменной module, часто встречающейся в модулях CommonJS. Доступен через import 'node:module' или require('node:module').

module.builtinModules

[История]

ВерсияИзменения
v23.5.0Список теперь также содержит модули только с префиксами.
v9.3.0, v8.10.0, v6.13.0Добавлено в: v9.3.0, v8.10.0, v6.13.0

Список имен всех модулей, предоставляемых Node.js. Может использоваться для проверки того, поддерживается ли модуль сторонней организацией или нет.

module в данном контексте — это не тот же объект, который предоставляется обёрткой модуля. Для доступа к нему необходимо подключить модуль Module:

js
// module.mjs
// В модуле ECMAScript
import { builtinModules as builtin } from 'node:module'
js
// module.cjs
// В модуле CommonJS
const builtin = require('node:module').builtinModules

module.createRequire(filename)

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

  • filename <string> | <URL> Имя файла, которое будет использоваться для построения функции require. Должно быть объектом URL файла, строкой URL файла или строкой абсолютного пути.
  • Возвращает: <require> Функция require
js
import { createRequire } from 'node:module'
const require = createRequire(import.meta.url)

// sibling-module.js — это модуль CommonJS.
const siblingModule = require('./sibling-module')

module.findPackageJSON(specifier[, base])

Добавлен в: v23.2.0

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

Стабильность: 1 Стабильность: 1.1 - Активная разработка

  • specifier <string> | <URL> Спецификатор модуля, для которого нужно получить package.json. При передаче голого спецификатора возвращается package.json в корне пакета. При передаче относительного спецификатора или абсолютного спецификатора возвращается ближайший родительский package.json.
  • base <string> | <URL> Абсолютное расположение (строка URL file: или путь FS) содержащего модуля. Для CJS используйте __filename (не __dirname!); для ESM используйте import.meta.url. Вам не нужно передавать его, если specifier является абсолютным спецификатором.
  • Возвращает: <string> | <undefined> Путь, если package.json найден. Когда startLocation является пакетом, корневой package.json пакета; когда относительный или неразрешённый, ближайший package.json к startLocation.
text
/path/to/project
  ├ packages/
    ├ bar/
      ├ bar.js
      └ package.json // name = '@foo/bar'
    └ qux/
      ├ node_modules/
        └ some-package/
          └ package.json // name = 'some-package'
      ├ qux.js
      └ package.json // name = '@foo/qux'
  ├ main.js
  └ package.json // name = '@foo'
js
// /path/to/project/packages/bar/bar.js
import { findPackageJSON } from 'node:module'

findPackageJSON('..', import.meta.url)
// '/path/to/project/package.json'
// Тот же результат при передаче абсолютного спецификатора:
findPackageJSON(new URL('../', import.meta.url))
findPackageJSON(import.meta.resolve('../'))

findPackageJSON('some-package', import.meta.url)
// '/path/to/project/packages/bar/node_modules/some-package/package.json'
// При передаче абсолютного спецификатора вы можете получить другой результат, если
// разрешённый модуль находится внутри подпапки, которая имеет вложенный `package.json`.
findPackageJSON(import.meta.resolve('some-package'))
// '/path/to/project/packages/bar/node_modules/some-package/some-subfolder/package.json'

findPackageJSON('@foo/qux', import.meta.url)
// '/path/to/project/packages/qux/package.json'
js
// /path/to/project/packages/bar/bar.js
const { findPackageJSON } = require('node:module')
const { pathToFileURL } = require('node:url')
const path = require('node:path')

findPackageJSON('..', __filename)
// '/path/to/project/package.json'
// Тот же результат при передаче абсолютного спецификатора:
findPackageJSON(pathToFileURL(path.join(__dirname, '..')))

findPackageJSON('some-package', __filename)
// '/path/to/project/packages/bar/node_modules/some-package/package.json'
// При передаче абсолютного спецификатора вы можете получить другой результат, если
// разрешённый модуль находится внутри подпапки, которая имеет вложенный `package.json`.
findPackageJSON(pathToFileURL(require.resolve('some-package')))
// '/path/to/project/packages/bar/node_modules/some-package/some-subfolder/package.json'

findPackageJSON('@foo/qux', __filename)
// '/path/to/project/packages/qux/package.json'

module.isBuiltin(moduleName)

Добавлено в: v18.6.0, v16.17.0

  • moduleName <string> имя модуля
  • Возвращает: <boolean> возвращает true, если модуль встроенный, иначе возвращает false
js
import { isBuiltin } from 'node:module'
isBuiltin('node:fs') // true
isBuiltin('fs') // true
isBuiltin('wss') // false

module.register(specifier[, parentURL][, options])

[История]

ВерсияИзменения
v20.8.0, v18.19.0Добавлена поддержка экземпляров URL WHATWG.
v20.6.0, v18.19.0Добавлено в: v20.6.0, v18.19.0

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

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

  • specifier <string> | <URL> Хуки настройки для регистрации; это должна быть та же строка, которая передавалась бы в import(), за исключением того, что если она относительная, она разрешается относительно parentURL.
  • parentURL <string> | <URL> Если вы хотите разрешить specifier относительно базового URL, такого как import.meta.url, вы можете передать этот URL сюда. По умолчанию: 'data:'
  • options <Object>
    • parentURL <string> | <URL> Если вы хотите разрешить specifier относительно базового URL, такого как import.meta.url, вы можете передать этот URL сюда. Это свойство игнорируется, если parentURL предоставлен во втором аргументе. По умолчанию: 'data:'
    • data <any> Любое произвольное, клонируемое значение JavaScript для передачи в хук initialize.
    • transferList <Object[]> перемещаемые объекты для передачи в хук initialize.

Зарегистрировать модуль, который экспортирует хуки, настраивающие поведение разрешения и загрузки модулей Node.js. См. Хуки настройки.

module.registerHooks(options)

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

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

Стабильность: 1 Стабильность: 1.1 - Активная разработка

Регистрация хуков, которые настраивают поведение разрешения и загрузки модулей Node.js. См. Хуки настройки.

module.stripTypeScriptTypes(code[, options])

Добавлено в: v23.2.0

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

Стабильность: 1 Стабильность: 1.1 - Активная разработка

  • code <string> Код, из которого нужно удалить аннотации типов.

  • options <Object>

    • mode <string> По умолчанию: 'strip'. Возможные значения:

    • 'strip' Удаляет только аннотации типов без преобразования функций TypeScript.

    • 'transform' Удаляет аннотации типов и преобразует функции TypeScript в JavaScript.

    • sourceMap <boolean> По умолчанию: false. Только когда mode равно 'transform', если true, будет создана карта исходного кода для преобразованного кода.

    • sourceUrl <string> Указывает URL источника, используемый в карте исходного кода.

  • Возвращает: <string> Код с удаленными аннотациями типов. module.stripTypeScriptTypes() удаляет аннотации типов из кода TypeScript. Его можно использовать для удаления аннотаций типов из кода TypeScript перед его запуском с помощью vm.runInContext() или vm.compileFunction(). По умолчанию, он выбросит ошибку, если код содержит функции TypeScript, требующие преобразования, такие как Enums, см. удаление типов для получения дополнительной информации. Когда mode равен 'transform', он также преобразует функции TypeScript в JavaScript, см. преобразование функций TypeScript для получения дополнительной информации. Когда mode равен 'strip', карты исходного кода не генерируются, поскольку местоположения сохраняются. Если sourceMap предоставлен, когда mode равен 'strip', будет выброшена ошибка.

ПРЕДУПРЕЖДЕНИЕ: Результат работы этой функции не следует считать стабильным в разных версиях Node.js из-за изменений в анализаторе TypeScript.

js
import { stripTypeScriptTypes } from 'node:module'
const code = 'const a: number = 1;'
const strippedCode = stripTypeScriptTypes(code)
console.log(strippedCode)
// Выводит: const a         = 1;
js
const { stripTypeScriptTypes } = require('node:module')
const code = 'const a: number = 1;'
const strippedCode = stripTypeScriptTypes(code)
console.log(strippedCode)
// Выводит: const a         = 1;

Если указан sourceUrl, он будет добавлен в виде комментария в конце результата:

js
import { stripTypeScriptTypes } from 'node:module'
const code = 'const a: number = 1;'
const strippedCode = stripTypeScriptTypes(code, { mode: 'strip', sourceUrl: 'source.ts' })
console.log(strippedCode)
// Выводит: const a         = 1\n\n//# sourceURL=source.ts;
js
const { stripTypeScriptTypes } = require('node:module')
const code = 'const a: number = 1;'
const strippedCode = stripTypeScriptTypes(code, { mode: 'strip', sourceUrl: 'source.ts' })
console.log(strippedCode)
// Выводит: const a         = 1\n\n//# sourceURL=source.ts;

Когда mode равно 'transform', код преобразуется в JavaScript:

js
import { stripTypeScriptTypes } from 'node:module'
const code = `
  namespace MathUtil {
    export const add = (a: number, b: number) => a + b;
  }`
const strippedCode = stripTypeScriptTypes(code, { mode: 'transform', sourceMap: true })
console.log(strippedCode)
// Выводит:
// var MathUtil;
// (function(MathUtil) {
//     MathUtil.add = (a, b)=>a + b;
// })(MathUtil || (MathUtil = {}));
// # sourceMappingURL=data:application/json;base64, ...
js
const { stripTypeScriptTypes } = require('node:module')
const code = `
  namespace MathUtil {
    export const add = (a: number, b: number) => a + b;
  }`
const strippedCode = stripTypeScriptTypes(code, { mode: 'transform', sourceMap: true })
console.log(strippedCode)
// Выводит:
// var MathUtil;
// (function(MathUtil) {
//     MathUtil.add = (a, b)=>a + b;
// })(MathUtil || (MathUtil = {}));
// # sourceMappingURL=data:application/json;base64, ...

module.syncBuiltinESMExports()

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

Метод module.syncBuiltinESMExports() обновляет все активные привязки для встроенных ES-модулей, чтобы они соответствовали свойствам экспорта CommonJS. Он не добавляет и не удаляет экспортированные имена из ES-модулей.

js
const fs = require('node:fs')
const assert = require('node:assert')
const { syncBuiltinESMExports } = require('node:module')

fs.readFile = newAPI

delete fs.readFileSync

function newAPI() {
  // ...
}

fs.newAPI = newAPI

syncBuiltinESMExports()

import('node:fs').then(esmFS => {
  // Он синхронизирует существующее свойство readFile с новым значением
  assert.strictEqual(esmFS.readFile, newAPI)
  // readFileSync был удален из требуемого fs
  assert.strictEqual('readFileSync' in fs, false)
  // syncBuiltinESMExports() не удаляет readFileSync из esmFS
  assert.strictEqual('readFileSync' in esmFS, true)
  // syncBuiltinESMExports() не добавляет имена
  assert.strictEqual(esmFS.newAPI, undefined)
})

Кэш компиляции модулей

[История]

ВерсияИзменения
v22.8.0Добавлены начальные JavaScript API для доступа во время выполнения.
v22.1.0Добавлено в: v22.1.0

Кэш компиляции модулей может быть включен с помощью метода module.enableCompileCache() или переменной окружения NODE_COMPILE_CACHE=dir. После его включения, всякий раз, когда Node.js компилирует модуль CommonJS или ECMAScript, он будет использовать сохраняемый на диске кэш кода V8 в указанном каталоге для ускорения компиляции. Это может замедлить первую загрузку графа модулей, но последующие загрузки того же графа модулей могут значительно ускориться, если содержимое модулей не изменится.

Чтобы очистить сгенерированный кэш компиляции на диске, просто удалите каталог кэша. Каталог кэша будет пересоздан при следующем использовании того же каталога для хранения кэша компиляции. Чтобы избежать заполнения диска устаревшим кэшем, рекомендуется использовать каталог в os.tmpdir(). Если кэш компиляции включен вызовом module.enableCompileCache() без указания каталога, Node.js будет использовать переменную окружения NODE_COMPILE_CACHE=dir, если она установлена, или по умолчанию path.join(os.tmpdir(), 'node-compile-cache'). Чтобы определить местоположение каталога кэша компиляции, используемого работающим экземпляром Node.js, используйте module.getCompileCacheDir().

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

Включенный кэш компиляции модулей может быть отключен переменной окружения NODE_DISABLE_COMPILE_CACHE=1. Это может быть полезно, когда кэш компиляции приводит к неожиданному или нежелательному поведению (например, менее точное покрытие тестов).

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

В настоящее время, когда кэш компиляции включен и модуль загружается заново, кэш кода генерируется из скомпилированного кода немедленно, но будет записан на диск только тогда, когда экземпляр Node.js собирается завершить работу. Это может измениться. Метод module.flushCompileCache() может быть использован для обеспечения того, чтобы накопленный кэш кода был записан на диск, если приложение хочет запустить другие экземпляры Node.js и позволить им совместно использовать кэш задолго до завершения работы родительского процесса.

module.constants.compileCacheStatus

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

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

Стабильность: 1 Стабильность: 1.1 - Активная разработка

Следующие константы возвращаются как поле status в объекте, возвращаемом функцией module.enableCompileCache(), для указания результата попытки включения кэша компиляции модулей.

КонстантаОписание
ENABLEDNode.js успешно включил кэш компиляции. Директория, используемая для хранения кэша компиляции, будет возвращена в поле directory возвращаемого объекта.
ALREADY_ENABLEDКэш компиляции уже был включен ранее, либо предыдущим вызовом module.enableCompileCache(), либо переменной окружения NODE_COMPILE_CACHE=dir. Директория, используемая для хранения кэша компиляции, будет возвращена в поле directory возвращаемого объекта.
FAILEDNode.js не удалось включить кэш компиляции. Это может быть вызвано отсутствием разрешения на использование указанной директории или различными видами ошибок файловой системы. Подробности об ошибке будут возвращены в поле message возвращаемого объекта.
DISABLEDNode.js не может включить кэш компиляции, поскольку установлена переменная окружения NODE_DISABLE_COMPILE_CACHE=1.

module.enableCompileCache([cacheDir])

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

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

Стабильность: 1 Стабильность: 1.1 - Активная разработка

  • cacheDir <строка> | <неопределено> Необязательный путь для указания директории, где будет храниться/извлекаться кэш компиляции.
  • Возвращает: <Объект>
    • status <целое число> Одно из значений module.constants.compileCacheStatus
    • message <строка> | <неопределено> Если Node.js не может включить кэш компиляции, это поле содержит сообщение об ошибке. Устанавливается только если status равно module.constants.compileCacheStatus.FAILED.
    • directory <строка> | <неопределено> Если кэш компиляции включен, это поле содержит директорию, где хранится кэш компиляции. Устанавливается только если status равно module.constants.compileCacheStatus.ENABLED или module.constants.compileCacheStatus.ALREADY_ENABLED.

Включает кэш компиляции модулей в текущем экземпляре Node.js.

Если cacheDir не указан, Node.js будет использовать директорию, указанную переменной окружения NODE_COMPILE_CACHE=dir, если она установлена, или path.join(os.tmpdir(), 'node-compile-cache') в противном случае. В общих случаях рекомендуется вызывать module.enableCompileCache() без указания cacheDir, чтобы директория могла быть переопределена переменной окружения NODE_COMPILE_CACHE при необходимости.

Поскольку кэш компиляции предназначен для тихой оптимизации, которая не требуется для функционирования приложения, этот метод разработан так, чтобы не выбрасывать исключения, когда кэш компиляции не может быть включен. Вместо этого он вернет объект, содержащий сообщение об ошибке в поле message для помощи в отладке. Если кэш компиляции включен успешно, поле directory в возвращаемом объекте содержит путь к директории, где хранится кэш компиляции. Поле status в возвращаемом объекте будет иметь одно из значений module.constants.compileCacheStatus, указывающее результат попытки включения кэша компиляции модулей.

Этот метод влияет только на текущий экземпляр Node.js. Для включения в дочерних рабочих потоках необходимо либо вызвать этот метод в дочерних рабочих потоках, либо установить значение process.env.NODE_COMPILE_CACHE в директорию кэша компиляции, чтобы поведение наследуется дочерними рабочими потоками. Директорию можно получить либо из поля directory, возвращаемого этим методом, либо с помощью module.getCompileCacheDir().

module.flushCompileCache()

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

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

Стабильность: 1 Стабильность: 1.1 — Активная разработка

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

module.getCompileCacheDir()

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

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

Стабильность: 1 Стабильность: 1.1 — Активная разработка

Крючки настройки

[История]

ВерсияИзменения
v23.5.0Добавлена поддержка синхронных и внутрипоточных крючков.
v20.6.0, v18.19.0Добавлен крючок initialize для замены globalPreload.
v18.6.0, v16.17.0Добавлена поддержка цепочек загрузчиков.
v16.12.0Удалены getFormat, getSource, transformSource и globalPreload; добавлены крючок load и крючок getGlobalPreload.
v8.8.0Добавлено в: v8.8.0

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

Стабильность: 1 Стабильность: 1.2 — Кандидат на выпуск (асинхронная версия) Стабильность: 1.1 — Активная разработка (синхронная версия)

В настоящее время поддерживаются два типа крючков настройки модулей:

Включение

Разрешение и загрузка модулей могут быть настроены с помощью:

Крючки могут быть зарегистрированы до запуска кода приложения с помощью флага --import или --require:

bash
node --import ./register-hooks.js ./my-app.js
node --require ./register-hooks.js ./my-app.js
js
// register-hooks.js
// Этот файл может быть подключен с помощью require() только в том случае, если он не содержит top-level await.
// Используйте module.register() для регистрации асинхронных крючков в выделенном потоке.
import { register } from 'node:module'
register('./hooks.mjs', import.meta.url)
js
// register-hooks.js
const { register } = require('node:module')
const { pathToFileURL } = require('node:url')
// Используйте module.register() для регистрации асинхронных крючков в выделенном потоке.
register('./hooks.mjs', pathToFileURL(__filename))
js
// Используйте module.registerHooks() для регистрации синхронных крючков в основном потоке.
import { registerHooks } from 'node:module'
registerHooks({
  resolve(specifier, context, nextResolve) {
    /* реализация */
  },
  load(url, context, nextLoad) {
    /* реализация */
  },
})
js
// Используйте module.registerHooks() для регистрации синхронных крючков в основном потоке.
const { registerHooks } = require('node:module')
registerHooks({
  resolve(specifier, context, nextResolve) {
    /* реализация */
  },
  load(url, context, nextLoad) {
    /* реализация */
  },
})

Файл, переданный в --import или --require, также может быть экспортом из зависимости:

bash
node --import some-package/register ./my-app.js
node --require some-package/register ./my-app.js

Где some-package имеет поле "exports", определяющее экспорт /register для сопоставления с файлом, который вызывает register(), например, пример register-hooks.js ниже.

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

В качестве альтернативы, register() и registerHooks() могут быть вызваны из точки входа, хотя для любого кода ESM, который должен быть запущен после регистрации крючков, необходимо использовать динамический import().

js
import { register } from 'node:module'

register('http-to-https', import.meta.url)

// Поскольку это динамический `import()`, крючки `http-to-https` будут запущены
// для обработки `./my-app.js` и любых других файлов, которые он импортирует или подключает.
await import('./my-app.js')
js
const { register } = require('node:module')
const { pathToFileURL } = require('node:url')

register('http-to-https', pathToFileURL(__filename))

// Поскольку это динамический `import()`, крючки `http-to-https` будут запущены
// для обработки `./my-app.js` и любых других файлов, которые он импортирует или подключает.
import('./my-app.js')

Крючки настройки будут работать для любых модулей, загруженных позже регистрации, и для модулей, на которые они ссылаются через import и встроенный require. Функция require, созданная пользователями с помощью module.createRequire(), может быть настроена только синхронными крючками.

В этом примере мы регистрируем крючки http-to-https, но они будут доступны только для последующих импортированных модулей — в данном случае my-app.js и всего, на что он ссылается через import или встроенный require в зависимостях CommonJS.

Если бы import('./my-app.js') был вместо этого статическим import './my-app.js', приложение было бы уже загружено до регистрации крючков http-to-https. Это связано со спецификацией модулей ES, где статические импорты сначала оцениваются с листьев дерева, а затем обратно к стволу. Внутри my-app.js могут быть статические импорты, которые не будут оценены до тех пор, пока my-app.js не будет импортирован динамически.

Если используются синхронные крючки, поддерживаются import, require и пользовательская функция require, созданная с помощью createRequire().

js
import { registerHooks, createRequire } from 'node:module'

registerHooks({
  /* реализация синхронных крючков */
})

const require = createRequire(import.meta.url)

// Синхронные крючки влияют на import, require() и пользовательскую функцию require(),
// созданную с помощью createRequire().
await import('./my-app.js')
require('./my-app-2.js')
js
const { register, registerHooks } = require('node:module')
const { pathToFileURL } = require('node:url')

registerHooks({
  /* реализация синхронных крючков */
})

const userRequire = createRequire(__filename)

// Синхронные крючки влияют на import, require() и пользовательскую функцию require(),
// созданную с помощью createRequire().
import('./my-app.js')
require('./my-app-2.js')
userRequire('./my-app-3.js')

Наконец, если все, что вы хотите сделать, это зарегистрировать крючки до запуска вашего приложения, и вы не хотите создавать для этого отдельный файл, вы можете передать URL-адрес data: в --import:

bash
node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("http-to-https", pathToFileURL("./"));' ./my-app.js

Цепочки

Можно вызывать register более одного раза:

js
// entrypoint.mjs
import { register } from 'node:module'

register('./foo.mjs', import.meta.url)
register('./bar.mjs', import.meta.url)
await import('./my-app.mjs')
js
// entrypoint.cjs
const { register } = require('node:module')
const { pathToFileURL } = require('node:url')

const parentURL = pathToFileURL(__filename)
register('./foo.mjs', parentURL)
register('./bar.mjs', parentURL)
import('./my-app.mjs')

В этом примере зарегистрированные хуки образуют цепочки. Эти цепочки выполняются по принципу LIFO (последний вошел — первый вышел). Если и foo.mjs, и bar.mjs определяют хук resolve, они будут вызваны следующим образом (обратите внимание на порядок справа налево): хук по умолчанию Node.js ← ./foo.mjs./bar.mjs (начиная с ./bar.mjs, затем ./foo.mjs, затем хук по умолчанию Node.js). То же самое относится ко всем другим хукам.

Зарегистрированные хуки также влияют на сам register. В этом примере bar.mjs будет разрешен и загружен с помощью хуков, зарегистрированных foo.mjs (потому что хуки foo уже будут добавлены в цепочку). Это позволяет создавать хуки на языках, отличных от JavaScript, при условии, что ранее зарегистрированные хуки транслируются в JavaScript.

Метод register не может быть вызван из модуля, который определяет хуки.

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

js
// entrypoint.mjs
import { registerHooks } from 'node:module'

const hook1 = {
  /* реализация хуков */
}
const hook2 = {
  /* реализация хуков */
}
// hook2 выполняется перед hook1.
registerHooks(hook1)
registerHooks(hook2)
js
// entrypoint.cjs
const { registerHooks } = require('node:module')

const hook1 = {
  /* реализация хуков */
}
const hook2 = {
  /* реализация хуков */
}
// hook2 выполняется перед hook1.
registerHooks(hook1)
registerHooks(hook2)

Взаимодействие с хуками настройки модулей

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

Метод register может использоваться для передачи данных в хук initialize. Передаваемые хуку данные могут включать передаваемые объекты, такие как порты.

js
import { register } from 'node:module'
import { MessageChannel } from 'node:worker_threads'

// В этом примере показано, как канал сообщений может использоваться для
// взаимодействия с хуками, отправляя `port2` в хуки.
const { port1, port2 } = new MessageChannel()

port1.on('message', msg => {
  console.log(msg)
})
port1.unref()

register('./my-hooks.mjs', {
  parentURL: import.meta.url,
  data: { number: 1, port: port2 },
  transferList: [port2],
})
js
const { register } = require('node:module')
const { pathToFileURL } = require('node:url')
const { MessageChannel } = require('node:worker_threads')

// В этом примере показано, как канал сообщений может использоваться для
// взаимодействия с хуками, отправляя `port2` в хуки.
const { port1, port2 } = new MessageChannel()

port1.on('message', msg => {
  console.log(msg)
})
port1.unref()

register('./my-hooks.mjs', {
  parentURL: pathToFileURL(__filename),
  data: { number: 1, port: port2 },
  transferList: [port2],
})

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

Хуки

Асинхронные хуки, принимаемые module.register()

Метод register может использоваться для регистрации модуля, который экспортирует набор хуков. Хуки — это функции, вызываемые Node.js для настройки процесса разрешения и загрузки модулей. Экспортируемые функции должны иметь определенные имена и сигнатуры, и они должны экспортироваться как именованные экспорты.

js
export async function initialize({ number, port }) {
  // Получает данные от `register`.
}

export async function resolve(specifier, context, nextResolve) {
  // Получает спецификатор `import` или `require` и преобразует его в URL.
}

export async function load(url, context, nextLoad) {
  // Получает решенный URL и возвращает исходный код для оценки.
}

Асинхронные хуки выполняются в отдельном потоке, изолированном от основного потока, в котором выполняется код приложения. Это означает, что это другая область. Поток хуков может быть завершен основным потоком в любое время, поэтому не следует зависеть от завершения асинхронных операций (таких как console.log). Они наследуются дочерними рабочими процессами по умолчанию.

Синхронные хуки, принимаемые module.registerHooks()

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

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

Стабильность: 1 Стабильность: 1.1 - Активная разработка

Метод module.registerHooks() принимает синхронные функции хуков. initialize() не поддерживается и не требуется, поскольку разработчик хука может просто запустить код инициализации непосредственно перед вызовом module.registerHooks().

js
function resolve(specifier, context, nextResolve) {
  // Преобразует спецификатор `import` или `require` в URL.
}

function load(url, context, nextLoad) {
  // Принимает результирующий URL и возвращает исходный код для оценки.
}

Синхронные хуки выполняются в том же потоке и той же сфере, где загружаются модули. В отличие от асинхронных хуков, они по умолчанию не наследуются дочерними рабочими потоками, хотя если хуки регистрируются с использованием файла, предварительно загруженного с помощью --import или --require, дочерние рабочие потоки могут наследовать предварительно загруженные скрипты через наследование process.execArgv. Подробнее см. в документации Worker.

В синхронных хуках пользователи могут ожидать, что console.log() завершится так же, как они ожидают завершения console.log() в коде модуля.

Конвенции хуков

Хуки являются частью цепочки, даже если эта цепочка состоит только из одного пользовательского хука и хука по умолчанию, который всегда присутствует. Функции хуков вложены: каждая из них всегда должна возвращать простой объект, и цепочка образуется в результате вызова каждой функцией next\<hookName\>(), которая является ссылкой на хук последующего загрузчика (в порядке LIFO).

Хук, возвращающий значение, в котором отсутствует необходимое свойство, вызывает исключение. Хук, который возвращается без вызова next\<hookName\>() и без возврата shortCircuit: true, также вызывает исключение. Эти ошибки помогают предотвратить непреднамеренные разрывы в цепочке. Верните shortCircuit: true из хука, чтобы сигнализировать о том, что цепочка преднамеренно заканчивается на вашем хуке.

initialize()

Добавлено в: v20.6.0, v18.19.0

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

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

  • data <any> Данные из register(loader, import.meta.url, { data }).

Хук initialize принимается только функцией register. registerHooks() его не поддерживает и не нуждается в нём, поскольку инициализация, выполненная для синхронных хуков, может быть запущена непосредственно перед вызовом registerHooks().

Хук initialize предоставляет способ определить пользовательскую функцию, которая выполняется в потоке хуков при инициализации модуля хуков. Инициализация происходит, когда модуль хуков регистрируется через register.

Этот хук может получать данные из вызова register, включая порты и другие передаваемые объекты. Возвращаемым значением initialize может быть <Promise>, в этом случае ожидание его завершения произойдёт перед возобновлением выполнения основного потока приложения.

Код настройки модуля:

js
// path-to-my-hooks.js

export async function initialize({ number, port }) {
  port.postMessage(`increment: ${number + 1}`)
}

Код вызывающей стороны:

js
import assert from 'node:assert'
import { register } from 'node:module'
import { MessageChannel } from 'node:worker_threads'

// В этом примере показано, как можно использовать канал сообщений для связи
// между основным (приложением) потоком и хуками, работающими в потоке хуков,
// отправляя `port2` в хук `initialize`.
const { port1, port2 } = new MessageChannel()

port1.on('message', msg => {
  assert.strictEqual(msg, 'increment: 2')
})
port1.unref()

register('./path-to-my-hooks.js', {
  parentURL: import.meta.url,
  data: { number: 1, port: port2 },
  transferList: [port2],
})
js
const assert = require('node:assert')
const { register } = require('node:module')
const { pathToFileURL } = require('node:url')
const { MessageChannel } = require('node:worker_threads')

// В этом примере показано, как можно использовать канал сообщений для связи
// между основным (приложением) потоком и хуками, работающими в потоке хуков,
// отправляя `port2` в хук `initialize`.
const { port1, port2 } = new MessageChannel()

port1.on('message', msg => {
  assert.strictEqual(msg, 'increment: 2')
})
port1.unref()

register('./path-to-my-hooks.js', {
  parentURL: pathToFileURL(__filename),
  data: { number: 1, port: port2 },
  transferList: [port2],
})

resolve(specifier, context, nextResolve)

[История]

ВерсияИзменения
v23.5.0Добавлена поддержка синхронных и внутрипоточных хуков.
v21.0.0, v20.10.0, v18.19.0Свойство context.importAssertions заменено на context.importAttributes. Использование старого имени по-прежнему поддерживается, но вызовет предупреждение о нестабильном функционале.
v18.6.0, v16.17.0Добавлена поддержка цепочки хуков resolve. Каждый хук должен либо вызвать nextResolve(), либо иметь свойство shortCircuit, установленное в true в возвращаемом значении.
v17.1.0, v16.14.0Добавлена поддержка импортных утверждений.

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

Стабильность: 1 Стабильность: 1.2 - Выпуск-кандидат (асинхронная версия) Стабильность: 1.1 - Активная разработка (синхронная версия)

  • specifier <строка>

  • context <Объект>

    • conditions <массив строк> Условия экспорта соответствующего файла package.json
    • importAttributes <Объект> Объект, пары ключ-значение которого представляют атрибуты для импортируемого модуля
    • parentURL <строка> | <неопределено> Модуль, импортирующий этот, или неопределено, если это точка входа Node.js
  • nextResolve <Функция> Следующий хук resolve в цепочке или хук resolve по умолчанию Node.js после последнего предоставленного пользователем хука resolve

  • Возвращаемое значение: <Объект> | <Promise> Асинхронная версия принимает либо объект, содержащий следующие свойства, либо Promise, который разрешится в такой объект. Синхронная версия принимает только объект, возвращаемый синхронно.

    • format <строка> | <null> | <неопределено> Подсказка для хука загрузки (может быть проигнорирована) 'builtin' | 'commonjs' | 'json' | 'module' | 'wasm'
    • importAttributes <Объект> | <неопределено> Атрибуты импорта, которые нужно использовать при кэшировании модуля (необязательно; если не указано, будет использоваться входной)
    • shortCircuit <неопределено> | <булево> Сигнал о том, что этот хук намерен прервать цепочку хуков resolve. По умолчанию: false
    • url <строка> Абсолютный URL, в который разрешается этот вход

Цепочка хуков resolve отвечает за указание Node.js, где найти и как кэшировать заданное выражение или оператор import или вызов require. Она может необязательно возвращать формат (например, 'module') в качестве подсказки для хука load. Если формат указан, хук load в конечном итоге отвечает за предоставление окончательного значения format (и он может игнорировать подсказку, предоставленную resolve); если resolve предоставляет format, необходим пользовательский хук load, даже если только для передачи значения в хук load по умолчанию Node.js.

Атрибуты типа импорта являются частью ключа кэша для сохранения загруженных модулей во внутреннем кэше модулей. Хук resolve отвечает за возвращение объекта importAttributes, если модуль должен кэшироваться с другими атрибутами, чем те, которые присутствовали в исходном коде.

Свойство conditions в context — это массив условий, которые будут использоваться для сопоставления условий экспорта пакетов для этого запроса на разрешение. Их можно использовать для поиска условных сопоставлений в другом месте или для изменения списка при вызове логики разрешения по умолчанию.

Текущие условия экспорта пакетов всегда находятся в массиве context.conditions, передаваемом в хук. Для гарантированного поведения разрешения спецификаторов модулей Node.js по умолчанию при вызове defaultResolve, массив context.conditions, передаваемый ему, должен включать все элементы массива context.conditions, первоначально переданного в хук resolve.

js
// Асинхронная версия, принимаемая module.register().
export async function resolve(specifier, context, nextResolve) {
  const { parentURL = null } = context

  if (Math.random() > 0.5) {
    // Некоторое условие.
    // Для некоторых или всех спецификаторов выполните некоторую пользовательскую логику для разрешения.
    // Всегда возвращайте объект вида {url: <строка>}.
    return {
      shortCircuit: true,
      url: parentURL ? new URL(specifier, parentURL).href : new URL(specifier).href,
    }
  }

  if (Math.random() < 0.5) {
    // Другое условие.
    // При вызове `defaultResolve` аргументы могут быть изменены. В этом
    // случае добавляется еще одно значение для сопоставления условных экспортов.
    return nextResolve(specifier, {
      ...context,
      conditions: [...context.conditions, 'another-condition'],
    })
  }

  // Передача следующему хуку в цепочке, который будет
  // хуком разрешения по умолчанию Node.js, если это последний указанный пользователем загрузчик.
  return nextResolve(specifier)
}
js
// Синхронная версия, принимаемая module.registerHooks().
function resolve(specifier, context, nextResolve) {
  // Аналогично асинхронному resolve() выше, так как он не имеет
  // никакой асинхронной логики.
}

load(url, context, nextLoad)

[История]

ВерсияИзменения
v23.5.0Добавлена поддержка синхронной и внутрипоточной версии.
v20.6.0Добавлена поддержка source с форматом commonjs.
v18.6.0, v16.17.0Добавлена поддержка цепочек хуков загрузки. Каждый хук должен либо вызвать nextLoad(), либо содержать свойство shortCircuit, установленное в true в его возвращаемом значении.

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

Стабильность: 1 Стабильность: 1.2 - Кандидат на выпуск (асинхронная версия) Стабильность: 1.1 - Активная разработка (синхронная версия)

  • url <строка> URL-адрес, возвращаемый цепочкой resolve

  • context <Объект>

  • nextLoad <Функция> Последующий хук load в цепочке или хук загрузки Node.js по умолчанию после последнего предоставленного пользователем хука load

  • Возвращает: <Объект> | <Promise> Асинхронная версия принимает либо объект, содержащий следующие свойства, либо Promise, который разрешится в такой объект. Синхронная версия принимает только объект, возвращаемый синхронно.

Хук load предоставляет способ определения пользовательского метода определения того, как следует интерпретировать, извлекать и анализировать URL-адрес. Он также отвечает за проверку атрибутов импорта.

Конечное значение format должно быть одним из следующих:

formatОписаниеДопустимые типы для source , возвращаемые load
'builtin'Загрузка встроенного модуля Node.jsНе применимо
'commonjs'Загрузка модуля Node.js CommonJS{ строка , ArrayBuffer , TypedArray , null , undefined }
'json'Загрузка JSON-файла{ строка , ArrayBuffer , TypedArray }
'module'Загрузка модуля ES{ строка , ArrayBuffer , TypedArray }
'wasm'Загрузка модуля WebAssembly{ ArrayBuffer , TypedArray }

Значение source игнорируется для типа 'builtin', поскольку в настоящее время невозможно заменить значение встроенного (основного) модуля Node.js.

Оговорка в асинхронном хуке load

При использовании асинхронного хука load пропуск или предоставление source для 'commonjs' имеет совершенно разные эффекты:

  • Когда предоставлен source, все вызовы require из этого модуля будут обработаны загрузчиком ESM с зарегистрированными хуками resolve и load; все вызовы require.resolve из этого модуля будут обработаны загрузчиком ESM с зарегистрированными хуками resolve; будет доступен только подмножество API CommonJS (например, нет require.extensions, нет require.cache, нет require.resolve.paths), и исправление ошибок в загрузчике модулей CommonJS применяться не будет.
  • Если source не определено или равно null, оно будет обработано загрузчиком модулей CommonJS, и вызовы require/require.resolve не будут проходить через зарегистрированные хуки. Это поведение для nullish source является временным — в будущем nullish source поддерживаться не будет.

Эти оговорки не применяются к синхронному хуку load, в этом случае полный набор API CommonJS доступен для настраиваемых модулей CommonJS, и require/require.resolve всегда проходят через зарегистрированные хуки.

Внутренняя асинхронная реализация load в Node.js, которая является значением next для последнего хука в цепочке load, возвращает null для source, когда format равен 'commonjs' для обратной совместимости. Вот пример хука, который будет использовать нестандартное поведение:

js
import { readFile } from 'node:fs/promises'

// Асинхронная версия, принимаемая module.register(). Это исправление не нужно
// для синхронной версии, принимаемой module.registerSync().
export async function load(url, context, nextLoad) {
  const result = await nextLoad(url, context)
  if (result.format === 'commonjs') {
    result.source ??= await readFile(new URL(result.responseURL ?? url))
  }
  return result
}

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

Если значение source текстового формата (т.е. 'json', 'module') не является строкой, оно преобразуется в строку с помощью util.TextDecoder.

Хук load предоставляет способ определить пользовательский метод для извлечения исходного кода разрешенного URL. Это позволит загрузчику потенциально избежать чтения файлов с диска. Его также можно использовать для сопоставления нераспознанного формата с поддерживаемым, например, yaml с module.

js
// Асинхронная версия, принимаемая module.register().
export async function load(url, context, nextLoad) {
  const { format } = context

  if (Math.random() > 0.5) {
    // Некое условие
    /*
      Для некоторых или всех URL выполните некоторую пользовательскую логику для извлечения источника.
      Всегда возвращайте объект вида {
        format: <строка>,
        source: <строка|буфер>,
      }.
    */
    return {
      format,
      shortCircuit: true,
      source: '...',
    }
  }

  // Передача следующему хуку в цепочке.
  return nextLoad(url)
}
js
// Синхронная версия, принимаемая module.registerHooks().
function load(url, context, nextLoad) {
  // Аналогично асинхронному load() выше, поскольку он не содержит
  // никакой асинхронной логики.
}

В более сложном сценарии это также может быть использовано для преобразования неподдерживаемого источника в поддерживаемый (см. Примеры ниже).

Примеры

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

Импорт по HTTPS

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

js
// https-hooks.mjs
import { get } from 'node:https'

export function load(url, context, nextLoad) {
  // Для загрузки JavaScript по сети нам нужно получить и
  // вернуть его.
  if (url.startsWith('https://')) {
    return new Promise((resolve, reject) => {
      get(url, res => {
        let data = ''
        res.setEncoding('utf8')
        res.on('data', chunk => (data += chunk))
        res.on('end', () =>
          resolve({
            // В этом примере предполагается, что весь JavaScript, предоставленный по сети, является кодом модуля ES.
            format: 'module',
            shortCircuit: true,
            source: data,
          })
        )
      }).on('error', err => reject(err))
    })
  }

  // Позвольте Node.js обрабатывать все остальные URL-адреса.
  return nextLoad(url)
}
js
// main.mjs
import { VERSION } from 'https://coffeescript.org/browser-compiler-modern/coffeescript.js'

console.log(VERSION)

С помощью предыдущего модуля хуков, запуск node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./https-hooks.mjs"));' ./main.mjs выводит текущую версию CoffeeScript в соответствии с модулем по URL в main.mjs.

Транспиляция

Источники, которые находятся в форматах, которые Node.js не понимает, могут быть преобразованы в JavaScript с помощью хука load.

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

Асинхронная версия
js
// coffeescript-hooks.mjs
import { readFile } from 'node:fs/promises'
import { dirname, extname, resolve as resolvePath } from 'node:path'
import { cwd } from 'node:process'
import { fileURLToPath, pathToFileURL } from 'node:url'
import coffeescript from 'coffeescript'

const extensionsRegex = /\.(coffee|litcoffee|coffee\.md)$/

export async function load(url, context, nextLoad) {
  if (extensionsRegex.test(url)) {
    // Файлы CoffeeScript могут быть как CommonJS, так и ES-модулями, поэтому мы хотим, чтобы любой
    // файл CoffeeScript обрабатывался Node.js так же, как файл .js в том же
    // месте. Чтобы определить, как Node.js будет интерпретировать произвольный файл .js,
    // выполните поиск по файловой системе ближайшего родительского файла package.json
    // и прочитайте его поле "type".
    const format = await getPackageType(url)

    const { source: rawSource } = await nextLoad(url, { ...context, format })
    // Этот хук преобразует исходный код CoffeeScript в исходный код JavaScript
    // для всех импортированных файлов CoffeeScript.
    const transformedSource = coffeescript.compile(rawSource.toString(), url)

    return {
      format,
      shortCircuit: true,
      source: transformedSource,
    }
  }

  // Позвольте Node.js обрабатывать все остальные URL-адреса.
  return nextLoad(url)
}

async function getPackageType(url) {
  // `url` - это только путь к файлу во время первой итерации, когда передается
  // результирующий URL-адрес из хука load()
  // фактический путь к файлу из load() будет содержать расширение файла, так как это
  // требуется спецификацией
  // эта простая проверка на истинность того, содержит ли `url` расширение файла, будет
  // работать для большинства проектов, но не охватывает некоторые пограничные случаи (например,
  // файлы без расширения или URL-адрес, заканчивающийся пробелом)
  const isFilePath = !!extname(url)
  // Если это путь к файлу, получите директорию, в которой он находится
  const dir = isFilePath ? dirname(fileURLToPath(url)) : url
  // Создайте путь к файлу package.json в том же каталоге,
  // который может существовать или не существовать
  const packagePath = resolvePath(dir, 'package.json')
  // Попробуйте прочитать возможно несуществующий package.json
  const type = await readFile(packagePath, { encoding: 'utf8' })
    .then(filestring => JSON.parse(filestring).type)
    .catch(err => {
      if (err?.code !== 'ENOENT') console.error(err)
    })
  // Если package.json существовал и содержал поле `type` со значением, вуаля
  if (type) return type
  // В противном случае (если не в корне) продолжите проверку следующего каталога вверх
  // Если в корне, остановитесь и верните false
  return dir.length > 1 && getPackageType(resolvePath(dir, '..'))
}
Синхронная версия
javascript
// coffeescript-sync-hooks.mjs
import { readFileSync } from 'node:fs/promises'
import { registerHooks } from 'node:module'
import { dirname, extname, resolve as resolvePath } from 'node:path'
import { cwd } from 'node:process'
import { fileURLToPath, pathToFileURL } from 'node:url'
import coffeescript from 'coffeescript'

const extensionsRegex = /\.(coffee|litcoffee|coffee\.md)$/

function load(url, context, nextLoad) {
  if (extensionsRegex.test(url)) {
    const format = getPackageType(url)

    const { source: rawSource } = nextLoad(url, { ...context, format })
    const transformedSource = coffeescript.compile(rawSource.toString(), url)

    return {
      format,
      shortCircuit: true,
      source: transformedSource,
    }
  }

  return nextLoad(url)
}

function getPackageType(url) {
  const isFilePath = !!extname(url)
  const dir = isFilePath ? dirname(fileURLToPath(url)) : url
  const packagePath = resolvePath(dir, 'package.json')

  let type
  try {
    const filestring = readFileSync(packagePath, { encoding: 'utf8' })
    type = JSON.parse(filestring).type
  } catch (err) {
    if (err?.code !== 'ENOENT') console.error(err)
  }
  if (type) return type
  return dir.length > 1 && getPackageType(resolvePath(dir, '..'))
}

registerHooks({ load })

Запуск хуков

coffeescript
# main.coffee {#maincoffee}
import { scream } from './scream.coffee'
console.log scream 'hello, world'

import { version } from 'node:process'
console.log "Brought to you by Node.js version #{version}"
coffeescript
# scream.coffee {#screamcoffee}
export scream = (str) -> str.toUpperCase()

С помощью вышеуказанных модулей хуков, запуск node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./coffeescript-hooks.mjs"));' ./main.coffee или node --import ./coffeescript-sync-hooks.mjs ./main.coffee приводит к тому, что main.coffee преобразуется в JavaScript после загрузки его исходного кода с диска, но до того, как Node.js начнет его исполнять; и так далее для любых файлов с расширениями .coffee, .litcoffee или .coffee.md, на которые ссылаются инструкции import в любом загруженном файле.

Карты импорта

В двух предыдущих примерах были определены хуки load. Это пример хука resolve. Этот хук модуля читает файл import-map.json, который определяет, какие спецификаторы следует переопределить на другие URL-адреса (это очень упрощенная реализация небольшого подмножества спецификации "карты импорта").

Асинхронная версия
js
// import-map-hooks.js
import fs from 'node:fs/promises'

const { imports } = JSON.parse(await fs.readFile('import-map.json'))

export async function resolve(specifier, context, nextResolve) {
  if (Object.hasOwn(imports, specifier)) {
    return nextResolve(imports[specifier], context)
  }

  return nextResolve(specifier, context)
}
Синхронная версия
js
// import-map-sync-hooks.js
import fs from 'node:fs/promises'
import module from 'node:module'

const { imports } = JSON.parse(fs.readFileSync('import-map.json', 'utf-8'))

function resolve(specifier, context, nextResolve) {
  if (Object.hasOwn(imports, specifier)) {
    return nextResolve(imports[specifier], context)
  }

  return nextResolve(specifier, context)
}

module.registerHooks({ resolve })
Использование хуков

С этими файлами:

js
// main.js
import 'a-module'
json
// import-map.json
{
  "imports": {
    "a-module": "./some-module.js"
  }
}
js
// some-module.js
console.log('some module!')

Выполнение node --import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register(pathToFileURL("./import-map-hooks.js"));' main.js или node --import ./import-map-sync-hooks.js main.js должно вывести some module!.

Поддержка Source Map v3

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

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

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

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

Для включения анализа карт исходного кода Node.js должен запускаться с флагом --enable-source-maps или с включенным покрытием кода путем установки NODE_V8_COVERAGE=dir.

js
// module.mjs
// В модуле ECMAScript
import { findSourceMap, SourceMap } from 'node:module'
js
// module.cjs
// В модуле CommonJS
const { findSourceMap, SourceMap } = require('node:module')

module.findSourceMap(path)

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

  • path <string>
  • Возвращает: <module.SourceMap> | <undefined> Возвращает module.SourceMap, если карта исходного кода найдена, undefined в противном случае.

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

Класс: module.SourceMap

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

new SourceMap(payload[, { lineLengths }]) {#new-sourcemappayload-{-linelengths-}}

Создает новый экземпляр sourceMap.

payload — это объект с ключами, соответствующими формату карты исходного кода v3:

lineLengths — это необязательный массив длин каждой строки в сгенерированном коде.

sourceMap.payload

Геттер для полезной нагрузки, используемой для построения экземпляра SourceMap.

sourceMap.findEntry(lineOffset, columnOffset)

  • lineOffset <number> Смещение номера строки (с нулевым индексом) в сгенерированном исходном коде
  • columnOffset <number> Смещение номера столбца (с нулевым индексом) в сгенерированном исходном коде
  • Возвращает: <Object>

Учитывая смещение номера строки и столбца в сгенерированном исходном файле, возвращает объект, представляющий диапазон SourceMap в исходном файле, если он найден, или пустой объект, если нет.

Возвращаемый объект содержит следующие ключи:

  • generatedLine: <number> Смещение номера строки начала диапазона в сгенерированном исходном коде
  • generatedColumn: <number> Смещение номера столбца начала диапазона в сгенерированном исходном коде
  • originalSource: <string> Имя файла исходного кода, как указано в SourceMap
  • originalLine: <number> Смещение номера строки начала диапазона в исходном коде
  • originalColumn: <number> Смещение номера столбца начала диапазона в исходном коде
  • name: <string>

Возвращаемое значение представляет собой необработанный диапазон, как он отображается в SourceMap, на основе смещений с нулевым индексом, не номеров строк и столбцов с индексом 1, как они отображаются в сообщениях об ошибках и объектах CallSite.

Чтобы получить соответствующие номера строк и столбцов с индексом 1 из номера строки и номера столбца, как они сообщаются стеками ошибок и объектами CallSite, используйте sourceMap.findOrigin(lineNumber, columnNumber)

sourceMap.findOrigin(lineNumber, columnNumber)

  • lineNumber <number> Номер строки (с учётом 1-го индекса) места вызова в сгенерированном исходном коде
  • columnNumber <number> Номер столбца (с учётом 1-го индекса) места вызова в сгенерированном исходном коде
  • Возвращает: <Object>

По заданным номеру строки (lineNumber) и номеру столбца (columnNumber) (с учётом 1-го индекса) места вызова в сгенерированном исходном коде, найти соответствующее местоположение места вызова в исходном коде.

Если указанные lineNumber и columnNumber не найдены ни в одной карте исходного кода, возвращается пустой объект. В противном случае возвращаемый объект содержит следующие ключи:

  • name: <string> | <undefined> Имя диапазона в карте исходного кода, если оно было предоставлено
  • fileName: <string> Имя файла исходного кода, как указано в SourceMap
  • lineNumber: <number> Номер строки (с учётом 1-го индекса) соответствующего места вызова в исходном коде
  • columnNumber: <number> Номер столбца (с учётом 1-го индекса) соответствующего места вызова в исходном коде