Spec-Zone.ru › Node.js

Модули: node:module API

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

Объект Module

  • <Объект>

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

module.builtinModules

Добавлен в: v9.3.0, v8.10.0, v6.13.0
  • <строка[]>

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

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

Модули MJS

// module.mjs
// In an ECMAScript module
import { builtinModules as builtin } from 'node:module';

Модули CJS

// module.cjs
// In a CommonJS module
const builtin = require('node:module').builtinModules;

module.createRequire(filename)

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

// sibling-module.js is a CommonJS module.
const siblingModule = require('./sibling-module'); copy

module.isBuiltin(moduleName)

Добавлен в: v18.6.0, v16.17.0
  • moduleName <строка> имя модуля
  • Возвращает: <логическое значение> возвращает true, если модуль встроенный, иначе возвращает false
import { isBuiltin } from 'node:module';
isBuiltin('node:fs'); // true
isBuiltin('fs'); // true
isBuiltin('wss'); // false copy

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

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

Добавлена поддержка экземпляров WHATWG URL.

v20.6.0, v18.19.0

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

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

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

module.syncBuiltinESMExports()

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

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

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) => {
  // It syncs the existing readFile property with the new value
  assert.strictEqual(esmFS.readFile, newAPI);
  // readFileSync has been deleted from the required fs
  assert.strictEqual('readFileSync' in fs, false);
  // syncBuiltinESMExports() does not remove readFileSync from esmFS
  assert.strictEqual('readFileSync' in esmFS, true);
  // syncBuiltinESMExports() does not add names
  assert.strictEqual(esmFS.newAPI, undefined);
}); copy

Настройка хуков

История
Версия Изменения
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.2 - Кандидат в релиз

Включение

Разрешение и загрузка модулей можно настроить, зарегистрировав файл, который экспортирует набор хуков. Это можно сделать, используя метод register из node:module, который можно запустить перед кодом вашего приложения, используя флаг --import:

node --import ./register-hooks.js ./my-app.js copy

Модули MJS

// register-hooks.js
import { register } from 'node:module';

register('./hooks.mjs', import.meta.url);

Модули CJS

// register-hooks.js
const { register } = require('node:module');
const { pathToFileURL } = require('node:url');

register('./hooks.mjs', pathToFileURL(__filename));

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

node --import some-package/register ./my-app.js copy

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

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

Модули MJS

import { register } from 'node:module';

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

// Because this is a dynamic `import()`, the `http-to-https` hooks will run
// to handle `./my-app.js` and any other files it imports or requires.
await import('./my-app.js');

Модули CJS

const { register } = require('node:module');
const { pathToFileURL } = require('node:url');

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

// Because this is a dynamic `import()`, the `http-to-https` hooks will run
// to handle `./my-app.js` and any other files it imports or requires.
import('./my-app.js');

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

my-app.js также может быть CommonJS. Хуки настройки будут работать для любых модулей, на которые он ссылается через import (и необязательно require).

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

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

Цепочки

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

Модули MJS

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

register('./foo.mjs', import.meta.url);
register('./bar.mjs', import.meta.url);
await import('./my-app.mjs');

Модули CJS

// 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 не может быть вызван из модуля, определяющего хуки.

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

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

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

Модули MJS

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

// This example demonstrates how a message channel can be used to
// communicate with the hooks, by sending `port2` to the hooks.
const { port1, port2 } = new MessageChannel();

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

register('./my-hooks.mjs', {
  parentURL: import.meta.url,
  data: { number: 1, port: port2 },
  transferList: [port2],
});

Модули CJS

const { register } = require('node:module');
const { pathToFileURL } = require('node:url');
const { MessageChannel } = require('node:worker_threads');

// This example showcases how a message channel can be used to
// communicate with the hooks, by sending `port2` to the hooks.
const { port1, port2 } = new MessageChannel();

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

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

Хуки

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

export async function initialize({ number, port }) {
  // Receives data from `register`.
}

export async function resolve(specifier, context, nextResolve) {
  // Take an `import` or `require` specifier and resolve it to a URL.
}

export async function load(url, context, nextLoad) {
  // Take a resolved URL and return the source code to be evaluated.
} copy

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

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

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

initialize()
Добавлен в: v20.6.0, v18.19.0
Уровень стабильности: 1.2 - Кандидат в релиз
  • data <любой> Данные из register(loader, import.meta.url, { data }).

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

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

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

// path-to-my-hooks.js

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

Код вызывающей программы:

Модули MJS

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

// This example showcases how a message channel can be used to communicate
// between the main (application) thread and the hooks running on the hooks
// thread, by sending `port2` to the `initialize` hook.
const { port1, port2 } = new MessageChannel();

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

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

Модули CJS

const assert = require('node:assert');
const { register } = require('node:module');
const { pathToFileURL } = require('node:url');
const { MessageChannel } = require('node:worker_threads');

// This example showcases how a message channel can be used to communicate
// between the main (application) thread and the hooks running on the hooks
// thread, by sending `port2` to the `initialize` hook.
const { port1, port2 } = new MessageChannel();

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

register('./path-to-my-hooks.js', {
  parentURL: pathToFileURL(__filename),
  data: { number: 1, port: port2 },
  transferList: [port2],
});
resolve(specifier, context, nextResolve)
История
Версия Изменения
v21.0.0, v20.10.0, v18.19.0

Свойство context.importAssertions заменено на context.importAttributes. Использование старого имени по-прежнему поддерживается и приведет к сообщению экспериментального предупреждения.

v18.6.0, v16.17.0

Добавлена поддержка цепочки хуков разрешения. Каждый хук должен либо вызвать nextResolve(), либо включать свойство shortCircuit, установленное в true, в своем возвращаемом значении.

v17.1.0, v16.14.0

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

Уровень стабильности: 1.2 - Кандидат в релиз
  • specifier <string>
  • context <Object>
    • conditions <string[]> Условия экспорта соответствующего package.json
    • importAttributes <Object> Объект, пары ключ-значение которого представляют атрибуты для импорта модуля
    • parentURL <string> | <undefined> Модуль, импортирующий этот, или undefined, если это точка входа Node.js
  • nextResolve <Function> Следующий resolve обработчик в цепочке или стандартный resolve обработчик Node.js после последнего пользовательского resolve обработчика
    • specifier <string>
    • context <Object>
  • Returns: <Object> | <Promise>
    • format <string> | <null> | <undefined> Подсказка для обработчика загрузки (может быть проигнорирована) 'builtin' | 'commonjs' | 'json' | 'module' | 'wasm'
    • importAttributes <Object> | <undefined> Атрибуты импорта для использования при кэшировании модуля (необязательно; если опущены, будет использован входной параметр)
    • shortCircuit <undefined> | <boolean> Сигнал, что этот обработчик намерен завершить цепочку resolve обработчиков. По умолчанию: false
    • url <string> Абсолютный URL, к которому приводит этот входной параметр

Предупреждение Несмотря на поддержку возвращаемых промисов и асинхронных функций, вызовы к resolve могут заблокировать основной поток, что повлияет на производительность.

Цепочка обработчиков 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 обработчик.

export async function resolve(specifier, context, nextResolve) {
  const { parentURL = null } = context;

  if (Math.random() > 0.5) { // Some condition.
    // For some or all specifiers, do some custom logic for resolving.
    // Always return an object of the form {url: <string>}.
    return {
      shortCircuit: true,
      url: parentURL ?
        new URL(specifier, parentURL).href :
        new URL(specifier).href,
    };
  }

  if (Math.random() < 0.5) { // Another condition.
    // When calling `defaultResolve`, the arguments can be modified. In this
    // case it's adding another value for matching conditional exports.
    return nextResolve(specifier, {
      ...context,
      conditions: [...context.conditions, 'another-condition'],
    });
  }

  // Defer to the next hook in the chain, which would be the
  // Node.js default resolve if this is the last user-specified loader.
  return nextResolve(specifier);
} copy
load(url, context, nextLoad)
История
Версия Изменения
v20.6.0

Добавлена поддержка source с форматом commonjs.

v18.6.0, v16.17.0

Добавлена поддержка цепочки обработчиков загрузки. Каждый обработчик должен либо вызвать nextLoad(), либо включить свойство shortCircuit, установленное в true, в своем результате.

Уровень стабильности: 1.2 - Кандидат в релиз
  • url <string> URL, возвращённый цепочкой resolve обработчиков
  • context <Object>
    • conditions <string[]> Условия экспорта соответствующего package.json
    • format <string> | <null> | <undefined> Формат, необязательно предоставленный цепочкой resolve обработчиков
    • importAttributes <Object>
  • nextLoad <Function> Следующий load обработчик в цепочке или стандартный load обработчик Node.js после последнего пользовательского load обработчика
    • specifier <string>
    • context <Object>
  • Returns: <Object>
    • format <string>
    • shortCircuit <undefined> | <boolean> Сигнал, что этот обработчик намерен завершить цепочку resolve обработчиков. По умолчанию: false
    • source <string> | <ArrayBuffer> | <TypedArray> Источник для оценки Node.js

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

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

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

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

Опускание или предоставление source для 'commonjs' имеет совершенно разные последствия:

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

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

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

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;
} copy

Предупреждение: Обработчик ESM load и именованные экспорты из модулей CommonJS несовместимы. Попытка их совместного использования приведет к пустому объекту из импорта. В будущем это может быть исправлено.

Эти типы соответствуют классам, определённым в ECMAScript.

  • Конкретный объект ArrayBuffer является SharedArrayBuffer.
  • Конкретный объект TypedArray является Uint8Array.

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

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

export async function load(url, context, nextLoad) {
  const { format } = context;

  if (Math.random() > 0.5) { // Some condition
    /*
      For some or all URLs, do some custom logic for retrieving the source.
      Always return an object of the form {
        format: <string>,
        source: <string|buffer>,
      }.
    */
    return {
      format,
      shortCircuit: true,
      source: '...',
    };
  }

  // Defer to the next hook in the chain.
  return nextLoad(url);
} copy

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

Примеры

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

Импорт из HTTPS

В текущей версии Node.js спецификаторы, начинающиеся с https:// являются экспериментальными (см. Импорт из HTTPS и HTTP).

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

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

export function load(url, context, nextLoad) {
  // For JavaScript to be loaded over the network, we need to fetch and
  // return it.
  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({
          // This example assumes all network-provided JavaScript is ES module
          // code.
          format: 'module',
          shortCircuit: true,
          source: data,
        }));
      }).on('error', (err) => reject(err));
    });
  }

  // Let Node.js handle all other URLs.
  return nextLoad(url);
} copy
// main.mjs
import { VERSION } from 'https://coffeescript.org/browser-compiler-modern/coffeescript.js';

console.log(VERSION); copy

Используя приведенный модуль обработчиков, выполнение 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; обработчики транспилятора должны использоваться только в целях разработки и тестирования.

// 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 files can be either CommonJS or ES modules, so we want any
    // CoffeeScript file to be treated by Node.js the same as a .js file at the
    // same location. To determine how Node.js would interpret an arbitrary .js
    // file, search up the file system for the nearest parent package.json file
    // and read its "type" field.
    const format = await getPackageType(url);

    const { source: rawSource } = await nextLoad(url, { ...context, format });
    // This hook converts CoffeeScript source code into JavaScript source code
    // for all imported CoffeeScript files.
    const transformedSource = coffeescript.compile(rawSource.toString(), url);

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

  // Let Node.js handle all other URLs.
  return nextLoad(url);
}

async function getPackageType(url) {
  // `url` is only a file path during the first iteration when passed the
  // resolved url from the load() hook
  // an actual file path from load() will contain a file extension as it's
  // required by the spec
  // this simple truthy check for whether `url` contains a file extension will
  // work for most projects but does not cover some edge-cases (such as
  // extensionless files or a url ending in a trailing space)
  const isFilePath = !!extname(url);
  // If it is a file path, get the directory it's in
  const dir = isFilePath ?
    dirname(fileURLToPath(url)) :
    url;
  // Compose a file path to a package.json in the same directory,
  // which may or may not exist
  const packagePath = resolvePath(dir, 'package.json');
  // Try to read the possibly nonexistent package.json
  const type = await readFile(packagePath, { encoding: 'utf8' })
    .then((filestring) => JSON.parse(filestring).type)
    .catch((err) => {
      if (err?.code !== 'ENOENT') console.error(err);
    });
  // If package.json existed and contained a `type` field with a value, voilà
  if (type) return type;
  // Otherwise, (if not at the root) continue checking the next directory up
  // If at the root, stop and return false
  return dir.length > 1 && getPackageType(resolvePath(dir, '..'));
} copy
# main.coffee
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}" copy
# scream.coffee
export scream = (str) -> str.toUpperCase() copy

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

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

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

// 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);
} copy

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

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

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

Поддержка карты исходного кода v3

Добавлена в: v13.7.0, v12.17.0
Уровень стабильности: 1 - Экспериментальная

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

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

Модули MJS

// module.mjs
// In an ECMAScript module
import { findSourceMap, SourceMap } from 'node:module';

Модули CJS

// module.cjs
// In a CommonJS module
const { findSourceMap, SourceMap } = require('node:module');

module.findSourceMap(path)

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

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

Класс: module.SourceMap

Добавлена в: v13.7.0, v12.17.0
new SourceMap(payload[, { lineLengths }])
  • payload <объект>
  • lineLengths <массив чисел>

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

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

  • file: <строка>
  • version: <число>
  • sources: <массив строк>
  • sourcesContent: <массив строк>
  • names: <массив строк>
  • mappings: <строка>
  • sourceRoot: <строка>

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

sourceMap.payload
  • Возвращает: <объект>

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

sourceMap.findEntry(lineOffset, columnOffset)
  • lineOffset <число> Смещение номера строки (с нуля) в сгенерированном исходном коде
  • columnOffset <число> Смещение номера столбца (с нуля) в сгенерированном исходном коде
  • Возвращает: <объект>

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

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

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

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

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

sourceMap.findOrigin(lineNumber, columnNumber)
  • lineNumber <число> Номер строки (с индексом 1) вызова в сгенерированном исходном коде
  • columnNumber <число> Номер столбца (с индексом 1) вызова в сгенерированном исходном коде
  • Возвращает: <объект>

На основе номера строки (с индексом 1) и номера столбца (с индексом 1) вызова в сгенерированном исходном коде находит соответствующее местоположение вызова в исходном коде.

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

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

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

Spec-Zone.ru

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