モジュール: node:module API#

Module オブジェクト#

CommonJS モジュールでよく見られる module 変数である Module のインスタンスと対話する際に、一般的なユーティリティメソッドを提供します。 import 'node:module' または require('node:module') を介してアクセスします。

module.builtinModules#

Node.js によって提供されるすべてのモジュールの名前のリストです。モジュールがサードパーティによってメンテナンスされているかどうかを確認するために使用できます。

ここでの module は、モジュールラッパーによって提供されるオブジェクトと同じではありません。アクセスするには、Module モジュールを require してください。

// module.mjs
// In an ECMAScript module
import { builtinModules as builtin } from 'node:module';// module.cjs
// In a CommonJS module
const builtin = require('node:module').builtinModules;

module.createRequire(filename)#

  • filename <string> | <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'); 

module.findPackageJSON(specifier[, base])#

安定性: 1.1 - Active Development

  • specifier <string> | <URL> package.json を取得するモジュールの指定子。ベア指定子 (bare specifier) を渡すと、パッケージのルートにある package.json が返されます。相対指定子 (relative specifier) または 絶対指定子 (absolute specifier) を渡すと、最も近い親の package.json が返されます。
  • base <string> | <URL> 含むモジュールの絶対的な場所 (file: URL 文字列またはファイルシステムパス)。CJS の場合は __filename (__dirname ではない!) を使用し、ESM の場合は import.meta.url を使用します。specifier絶対指定子 の場合は渡す必要はありません。
  • 戻り値: <string> | <undefined> package.json が見つかった場合はそのパス。specifier がパッケージの場合、そのパッケージのルートの package.json が返されます。相対指定子または未解決の指定子の場合、specifier に最も近い package.json が返されます。

注意: これを使用してモジュールのフォーマットを判断しようとしないでください。その判断には多くの要因が影響します。package.json の type フィールドは最も決定的なものではありません (例: ファイル拡張子がそれを上書きし、ローダーフックがさらにそれを上書きします)。

注意: これは現在、組み込みのデフォルトリゾルバのみを利用します。resolve カスタマイズフックが登録されていても、解決には影響しません。これは将来変更される可能性があります。

/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' 
// /path/to/project/packages/bar/bar.js
import { findPackageJSON } from 'node:module';

findPackageJSON('..', import.meta.url);
// '/path/to/project/package.json'
// Same result when passing an absolute specifier instead:
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'
// When passing an absolute specifier, you might get a different result if the
// resolved module is inside a subfolder that has nested `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'// /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'
// Same result when passing an absolute specifier instead:
findPackageJSON(pathToFileURL(path.join(__dirname, '..')));

findPackageJSON('some-package', __filename);
// '/path/to/project/packages/bar/node_modules/some-package/package.json'
// When passing an absolute specifier, you might get a different result if the
// resolved module is inside a subfolder that has nested `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)#

  • moduleName <string> モジュール名
  • 戻り値: <boolean> モジュールが組み込みの場合は true を、そうでない場合は false を返します。
import { isBuiltin } from 'node:module';
isBuiltin('node:fs'); // true
isBuiltin('fs'); // true
isBuiltin('wss'); // false 

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

安定性: 1.2 - Release candidate

  • specifier <string> | <URL> 登録するカスタマイズフック。これは import() に渡すのと同じ文字列であるべきですが、相対パスの場合は parentURL を基準に解決されます。
  • parentURL <string> | <URL> specifierimport.meta.url のようなベース URL を基準に解決したい場合は、その URL をここに渡すことができます。デフォルト: 'data:'
  • options <Object>
    • parentURL <string> | <URL> specifierimport.meta.url のようなベース URL を基準に解決したい場合は、その URL をここに渡すことができます。このプロパティは、parentURL が第 2 引数として指定された場合は無視されます。デフォルト: 'data:'
    • data <any> initialize フックに渡す任意のクローン可能な JavaScript 値。
    • transferList <Object[]> initialize フックに渡す 転送可能オブジェクト

Node.js のモジュール解決と読み込みの動作をカスタマイズする フックをエクスポートするモジュールを登録します。カスタマイズフック を参照してください。

この機能は、パーミッションモデルと共に使用する場合、--allow-worker が必要です。

module.registerHooks(options)#

安定性: 1.1 - Active development

Node.js のモジュール解決と読み込みの動作をカスタマイズする フックを登録します。カスタマイズフック を参照してください。

module.stripTypeScriptTypes(code[, options])#

安定性: 1.2 - Release candidate

  • code <string> 型アノテーションを削除する対象のコード。
  • options <Object>
    • mode <string> デフォルト: 'strip'。指定可能な値は以下の通りです。
      • 'strip' TypeScript の機能変換を行わずに、型アノテーションのみを削除します。
      • 'transform' 型アノテーションを削除し、TypeScript の機能を JavaScript に変換します。
    • sourceMap <boolean> デフォルト: falsemode'transform' の場合にのみ有効です。true の場合、変換されたコードのソースマップが生成されます。
    • sourceUrl <string> ソースマップで使用されるソース URL を指定します。
  • 戻り値: <string> 型アノテーションが削除されたコード。module.stripTypeScriptTypes() は TypeScript コードから型アノテーションを削除します。これは vm.runInContext()vm.compileFunction() で実行する前に TypeScript コードから型アノテーションを削除するために使用できます。デフォルトでは、コードに Enums のような変換が必要な TypeScript の機能が含まれている場合、エラーをスローします。詳細については 型削除 を参照してください。mode'transform' の場合、TypeScript の機能を JavaScript に変換もします。詳細については TypeScript 機能の変換 を参照してください。mode'strip' の場合、位置情報が保持されるためソースマップは生成されません。mode'strip' の場合に sourceMap が指定されると、エラーがスローされます。

警告: この関数の出力は、TypeScript パーサーの変更により、Node.js のバージョン間で安定しているとは見なされません。

import { stripTypeScriptTypes } from 'node:module';
const code = 'const a: number = 1;';
const strippedCode = stripTypeScriptTypes(code);
console.log(strippedCode);
// Prints: const a         = 1;const { stripTypeScriptTypes } = require('node:module');
const code = 'const a: number = 1;';
const strippedCode = stripTypeScriptTypes(code);
console.log(strippedCode);
// Prints: const a         = 1;

sourceUrl が指定された場合、出力の末尾にコメントとして追加されます。

import { stripTypeScriptTypes } from 'node:module';
const code = 'const a: number = 1;';
const strippedCode = stripTypeScriptTypes(code, { mode: 'strip', sourceUrl: 'source.ts' });
console.log(strippedCode);
// Prints: const a         = 1\n\n//# sourceURL=source.ts;const { stripTypeScriptTypes } = require('node:module');
const code = 'const a: number = 1;';
const strippedCode = stripTypeScriptTypes(code, { mode: 'strip', sourceUrl: 'source.ts' });
console.log(strippedCode);
// Prints: const a         = 1\n\n//# sourceURL=source.ts;

mode'transform' の場合、コードは JavaScript に変換されます。

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);
// Prints:
// var MathUtil;
// (function(MathUtil) {
//     MathUtil.add = (a, b)=>a + b;
// })(MathUtil || (MathUtil = {}));
// # sourceMappingURL=data:application/json;base64, ...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);
// Prints:
// var MathUtil;
// (function(MathUtil) {
//     MathUtil.add = (a, b)=>a + b;
// })(MathUtil || (MathUtil = {}));
// # sourceMappingURL=data:application/json;base64, ...

module.syncBuiltinESMExports()#

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

モジュールコンパイルキャッシュ#

モジュールコンパイルキャッシュは、module.enableCompileCache() メソッドまたは NODE_COMPILE_CACHE=dir 環境変数のいずれかを使用して有効にできます。有効にすると、Node.js が CommonJS、ECMAScript モジュール、または TypeScript モジュールをコンパイルするたびに、指定されたディレクトリに永続化されたディスク上の V8 コードキャッシュを使用してコンパイルを高速化します。これにより、モジュールグラフの初回読み込みは遅くなる可能性がありますが、モジュールの内容が変更されない場合、同じモジュールグラフの以降の読み込みは大幅に高速化される可能性があります。

ディスク上に生成されたコンパイルキャッシュをクリーンアップするには、キャッシュディレクトリを削除するだけです。キャッシュディレクトリは、次に同じディレクトリがコンパイルキャッシュストレージとして使用されるときに再作成されます。ディスクが古いキャッシュでいっぱいになるのを避けるため、os.tmpdir() の下のディレクトリを使用することをお勧めします。コンパイルキャッシュが directory を指定せずに module.enableCompileCache() の呼び出しによって有効にされた場合、Node.js は NODE_COMPILE_CACHE=dir 環境変数が設定されていればそれを使用し、そうでなければ path.join(os.tmpdir(), 'node-compile-cache') をデフォルトとします。実行中の Node.js インスタンスによって使用されているコンパイルキャッシュディレクトリを見つけるには、module.getCompileCacheDir() を使用します。

有効化されたモジュールコンパイルキャッシュは、NODE_DISABLE_COMPILE_CACHE=1 環境変数によって無効にすることができます。これは、コンパイルキャッシュが予期しない、または望ましくない動作(例:精度の低いテストカバレッジ)を引き起こす場合に便利です。

現時点では、コンパイルキャッシュが有効でモジュールが新たに読み込まれると、コードキャッシュはコンパイルされたコードから直ちに生成されますが、ディスクに書き込まれるのは Node.js インスタンスが終了する直前です。これは変更される可能性があります。module.flushCompileCache() メソッドを使用して、蓄積されたコードキャッシュがディスクにフラッシュされることを保証できます。これは、アプリケーションが他の Node.js インスタンスを生成し、親が終了するずっと前にキャッシュを共有させたい場合に便利です。

コンパイルキャッシュのポータビリティ#

デフォルトでは、キャッシュ対象モジュールの絶対パスが変更されるとキャッシュは無効になります。プロジェクトディレクトリを移動した後もキャッシュを機能させ続けるには、ポータブルコンパイルキャッシュを有効にします。これにより、キャッシュディレクトリに対する相対的なレイアウトが同じままであれば、以前にコンパイルされたモジュールを異なるディレクトリの場所で再利用できます。これはベストエフォートベースで行われます。Node.js がキャッシュディレクトリに対するモジュールの場所を計算できない場合、そのモジュールはキャッシュされません。

ポータブルモードを有効にするには 2 つの方法があります。

  1. module.enableCompileCache() の portable オプションを使用する。

    // Non-portable cache (default): cache breaks if project is moved
    module.enableCompileCache({ directory: '/path/to/cache/storage/dir' });
    
    // Portable cache: cache works after the project is moved
    module.enableCompileCache({ directory: '/path/to/cache/storage/dir', portable: true }); 
  2. 環境変数を設定する: NODE_COMPILE_CACHE_PORTABLE=1

コンパイルキャッシュの制限#

現在、コンパイルキャッシュを V8 JavaScript コードカバレッジと共に使用する場合、コードキャッシュからデシリアライズされた関数では、V8 によって収集されるカバレッジの精度が低下する可能性があります。正確なカバレッジを生成するためにテストを実行する際には、これをオフにすることをお勧めします。

あるバージョンの Node.js で生成されたコンパイルキャッシュは、別のバージョンの Node.js では再利用できません。同じベースディレクトリがキャッシュの永続化に使用された場合、異なるバージョンの Node.js によって生成されたキャッシュは別々に保存されるため、共存できます。

module.constants.compileCacheStatus#

安定性: 1.1 - Active Development

以下の定数は、モジュールコンパイルキャッシュを有効にする試みの結果を示すために、module.enableCompileCache() によって返されるオブジェクトの status フィールドとして返されます。

定数 説明
ENABLED Node.js はコンパイルキャッシュを正常に有効にしました。コンパイルキャッシュを保存するために使用されるディレクトリは、返されたオブジェクトの directory フィールドで返されます。
ALREADY_ENABLED コンパイルキャッシュは、以前の module.enableCompileCache() の呼び出し、または NODE_COMPILE_CACHE=dir 環境変数のいずれかによって、すでに有効にされています。コンパイルキャッシュを保存するために使用されるディレクトリは、返されたオブジェクトの directory フィールドで返されます。
FAILED Node.js はコンパイルキャッシュの有効化に失敗しました。これは、指定されたディレクトリを使用する権限がないことや、さまざまな種類のファイルシステムエラーが原因である可能性があります。失敗の詳細は、返されたオブジェクトの message フィールドで返されます。
DISABLED 環境変数 NODE_DISABLE_COMPILE_CACHE=1 が設定されているため、Node.js はコンパイルキャッシュを有効にできません。

module.enableCompileCache([options])#

安定性: 1.1 - Active Development

  • options <string> | <Object> オプション。文字列が渡された場合、それは options.directory と見なされます。
    • directory <string> オプション。コンパイルキャッシュを保存するディレクトリ。指定されない場合、NODE_COMPILE_CACHE=dir 環境変数で指定されたディレクトリが設定されていれば使用され、そうでなければ path.join(os.tmpdir(), 'node-compile-cache') が使用されます。
    • portable <boolean> オプション。true の場合、プロジェクトディレクトリが移動してもキャッシュが再利用できるように、ポータブルコンパイルキャッシュを有効にします。これはベストエフォート機能です。指定されない場合、環境変数 NODE_COMPILE_CACHE_PORTABLE=1 が設定されているかどうかに依存します。
  • 戻り値: <Object>
    • status <integer> module.constants.compileCacheStatus のいずれか
    • message <string> | <undefined> Node.js がコンパイルキャッシュを有効にできない場合、これにはエラーメッセージが含まれます。statusmodule.constants.compileCacheStatus.FAILED の場合にのみ設定されます。
    • directory <string> | <undefined> コンパイルキャッシュが有効になっている場合、これにはコンパイルキャッシュが保存されているディレクトリが含まれます。statusmodule.constants.compileCacheStatus.ENABLED または module.constants.compileCacheStatus.ALREADY_ENABLED の場合にのみ設定されます。

現在の Node.js インスタンスで モジュールコンパイルキャッシュを有効にします。

一般的なユースケースでは、options.directory を指定せずに module.enableCompileCache() を呼び出すことが推奨されます。これにより、必要に応じて NODE_COMPILE_CACHE 環境変数でディレクトリを上書きできます。

コンパイルキャッシュはミッションクリティカルではない最適化であるため、このメソッドはコンパイルキャッシュを有効にできない場合に例外をスローしないように設計されています。代わりに、デバッグに役立つエラーメッセージを含むオブジェクトを message フィールドに返します。コンパイルキャッシュが正常に有効化された場合、返されるオブジェクトの directory フィールドにはコンパイルキャッシュが保存されているディレクトリのパスが含まれます。返されるオブジェクトの status フィールドは、モジュールコンパイルキャッシュを有効にする試みの結果を示す module.constants.compileCacheStatus の値のいずれかになります。

このメソッドは現在の Node.js インスタンスにのみ影響します。子ワーカースレッドで有効にするには、子ワーカースレッドでもこのメソッドを呼び出すか、process.env.NODE_COMPILE_CACHE の値をコンパイルキャッシュディレクトリに設定して、その動作が子ワーカーに継承されるようにします。ディレクトリは、このメソッドが返す directory フィールドから、または module.getCompileCacheDir() で取得できます。

module.flushCompileCache()#

安定性: 1.1 - Active Development

現在の Node.js インスタンスで既に読み込まれたモジュールから蓄積された モジュールコンパイルキャッシュをディスクにフラッシュします。これは、すべてのフラッシュファイルシステム操作が成功したかどうかにかかわらず、完了した後に返ります。エラーがあった場合、コンパイルキャッシュのミスがアプリケーションの実際の操作に干渉すべきではないため、これはサイレントに失敗します。

module.getCompileCacheDir()#

安定性: 1.1 - Active Development

カスタマイズフック#

安定性: 1.2 - リリース候補 (非同期版) 安定性: 1.1 - 活発な開発 (同期版)

現在サポートされているモジュールカスタマイズフックには 2 種類あります。

  1. module.register(specifier[, parentURL][, options]) は、非同期のフック関数をエクスポートするモジュールを受け取ります。これらの関数は、別のローダースレッドで実行されます。
  2. module.registerHooks(options) は、モジュールが読み込まれるスレッドで直接実行される同期フック関数を受け取ります。

有効化#

モジュールの解決と読み込みは、以下によってカスタマイズできます。

  1. node:moduleregister メソッドを使用して、非同期フック関数のセットをエクスポートするファイルを登録する。
  2. node:moduleregisterHooks メソッドを使用して、同期フック関数のセットを登録する。

フックは、--import または --require フラグを使用して、アプリケーションコードが実行される前に登録できます。

node --import ./register-hooks.js ./my-app.js
node --require ./register-hooks.js ./my-app.js 
// register-hooks.js
// This file can only be require()-ed if it doesn't contain top-level await.
// Use module.register() to register asynchronous hooks in a dedicated thread.
import { register } from 'node:module';
register('./hooks.mjs', import.meta.url);// register-hooks.js
const { register } = require('node:module');
const { pathToFileURL } = require('node:url');
// Use module.register() to register asynchronous hooks in a dedicated thread.
register('./hooks.mjs', pathToFileURL(__filename));
// Use module.registerHooks() to register synchronous hooks in the main thread.
import { registerHooks } from 'node:module';
registerHooks({
  resolve(specifier, context, nextResolve) { /* implementation */ },
  load(url, context, nextLoad) { /* implementation */ },
});// Use module.registerHooks() to register synchronous hooks in the main thread.
const { registerHooks } = require('node:module');
registerHooks({
  resolve(specifier, context, nextResolve) { /* implementation */ },
  load(url, context, nextLoad) { /* implementation */ },
});

--import または --require に渡されるファイルは、依存関係からのエクスポートでもかまいません。

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

ここで some-package は、/register エクスポートを、以下の register-hooks.js の例のように register() を呼び出すファイルにマッピングする "exports" フィールドを持っています。

--import または --require を使用することで、アプリケーションのエントリーポイントやデフォルトでワーカースレッドを含む、すべてのアプリケーションファイルがインポートされる前にフックが登録されることが保証されます。

あるいは、register()registerHooks() はエントリーポイントから呼び出すこともできますが、フックが登録された後に実行されるべき ESM コードに対しては、動的な import() を使用する必要があります。

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');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');

カスタマイズフックは、登録後およびそれらが import や組み込みの require を介して参照するモジュールに対して実行されます。ユーザーが module.createRequire() を使用して作成した require 関数は、同期フックによってのみカスタマイズできます。

この例では、http-to-https フックを登録していますが、これらは後続でインポートされるモジュール、この場合は my-app.js とそれが import または CommonJS 依存関係の組み込み require を介して参照するものに対してのみ利用可能です。

もし import('./my-app.js') が静的な import './my-app.js' だったら、アプリは http-to-https フックが登録される 既に読み込まれていたでしょう。これは ES モジュールの仕様によるもので、静的インポートはツリーの葉から先に評価され、次に幹に戻ります。my-app.js に静的インポートがあっても、それらは my-app.js が動的にインポートされるまで評価されません。

同期フックが使用される場合、importrequire、および createRequire() を使用して作成されたユーザー require の両方がサポートされます。

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

registerHooks({ /* implementation of synchronous hooks */ });

const require = createRequire(import.meta.url);

// The synchronous hooks affect import, require() and user require() function
// created through createRequire().
await import('./my-app.js');
require('./my-app-2.js');const { register, registerHooks } = require('node:module');
const { pathToFileURL } = require('node:url');

registerHooks({ /* implementation of synchronous hooks */ });

const userRequire = createRequire(__filename);

// The synchronous hooks affect import, require() and user require() function
// created through createRequire().
import('./my-app.js');
require('./my-app-2.js');
userRequire('./my-app-3.js');

最後に、アプリが実行される前にフックを登録したいだけで、そのために別のファイルを作成したくない場合は、--importdata: URL を渡すことができます。

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

チェイニング#

register を複数回呼び出すことが可能です。

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

register('./foo.mjs', import.meta.url);
register('./bar.mjs', import.meta.url);
await import('./my-app.mjs');// 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.mjsbar.mjs の両方が resolve フックを定義している場合、それらは次のように呼び出されます (右から左へ注意): Node のデフォルト ← ./foo.mjs./bar.mjs (./bar.mjs から始まり、次に ./foo.mjs、そして Node.js のデフォルト)。これは他のすべてのフックにも適用されます。

登録されたフックは register 自体にも影響します。この例では、bar.mjsfoo.mjs によって登録されたフックを介して解決され、読み込まれます (foo のフックは既にチェーンに追加されているため)。これにより、先に登録されたフックが JavaScript にトランスパイルされる限り、非 JavaScript 言語でフックを書くことなどが可能になります。

register メソッドは、フックを定義するモジュール内からは呼び出せません。

registerHooks のチェイニングも同様に機能します。同期フックと非同期フックが混在している場合、同期フックは常に非同期フックの実行が始まる前に実行されます。つまり、最後に実行される同期フックの次のフックには、非同期フックの呼び出しが含まれます。

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

const hook1 = { /* implementation of hooks */ };
const hook2 = { /* implementation of hooks */ };
// hook2 run before hook1.
registerHooks(hook1);
registerHooks(hook2);// entrypoint.cjs
const { registerHooks } = require('node:module');

const hook1 = { /* implementation of hooks */ };
const hook2 = { /* implementation of hooks */ };
// hook2 run before hook1.
registerHooks(hook1);
registerHooks(hook2);

モジュールカスタマイズフックとの通信#

非同期フックは、アプリケーションコードを実行するメインスレッドとは別の専用スレッドで実行されます。これは、グローバル変数を変更しても他のスレッドに影響しないことを意味し、スレッド間で通信するにはメッセージチャネルを使用する必要があります。

register メソッドを使用して、initialize フックにデータを渡すことができます。フックに渡されるデータには、ポートなどの転送可能なオブジェクトを含めることができます。

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);
});
port1.unref();

register('./my-hooks.mjs', {
  parentURL: import.meta.url,
  data: { number: 1, port: port2 },
  transferList: [port2],
});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);
});
port1.unref();

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

同期モジュールフックは、アプリケーションコードが実行されるのと同じスレッドで実行されます。これらは、メインスレッドからアクセスされるコンテキストのグローバルを直接変更できます。

フック#

module.register() で受け入れられる非同期フック#

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.
} 

非同期フックは、アプリケーションコードが実行されるメインスレッドから分離された別のスレッドで実行されます。これは、それが異なるレルム (realm) であることを意味します。フックスレッドはいつでもメインスレッドによって終了させられる可能性があるため、非同期操作 (console.log など) が完了することに依存しないでください。これらはデフォルトで子ワーカーに継承されます。

module.registerHooks() で受け入れられる同期フック#

安定性: 1.1 - Active development

module.registerHooks() メソッドは、同期フック関数を受け入れます。initialize() はサポートされておらず、また必要もありません。なぜなら、フックの実装者は module.registerHooks() の呼び出しの直前に初期化コードを直接実行できるからです。

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

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

同期フックは、モジュールが読み込まれるのと同じスレッドと同じレルム (realm) で実行されます。非同期フックとは異なり、これらはデフォルトで子ワーカースレッドに継承されません。ただし、フックが --import または --require によってプリロードされたファイルを使用して登録されている場合、子ワーカースレッドは process.execArgv の継承を介してプリロードされたスクリプトを継承できます。詳細は Worker のドキュメントを参照してください。

同期フックでは、ユーザーはモジュールコード内の console.log() が完了するのと同じように、console.log() が完了することを期待できます。

フックの規約#

フックはチェーンの一部です。たとえそのチェーンが1つのカスタム (ユーザー提供) フックと、常に存在するデフォルトフックだけで構成されていてもです。フック関数はネストします。各フックは常にプレーンなオブジェクトを返す必要があり、各関数が next<hookName>() (後入れ先出し順で後続のローダーのフックへの参照) を呼び出す結果としてチェイニングが発生します。

必須のプロパティを欠いた値を返すフックは例外をトリガーします。next<hookName>() を呼び出さず、かつ shortCircuit: true を返さずにリターンするフックも例外をトリガーします。これらのエラーは、チェーンの意図しない切断を防ぐためのものです。チェーンが意図的にあなたのフックで終了することを示すには、フックから shortCircuit: true を返してください。

initialize()#

安定性: 1.2 - Release candidate

  • data <any> register(loader, import.meta.url, { data }) からのデータ。

initialize フックは register によってのみ受け入れられます。registerHooks() は、同期フックのために行われる初期化は registerHooks() の呼び出しの直前に直接実行できるため、それをサポートしておらず、必要もありません。

initialize フックは、フックモジュールが初期化されるときにフックスレッドで実行されるカスタム関数を定義する方法を提供します。初期化は、フックモジュールが register を介して登録されるときに発生します。

このフックは、ポートやその他の転送可能なオブジェクトを含むデータを register の呼び出しから受け取ることができます。initialize の戻り値は <Promise> であってもよく、その場合はメインアプリケーションスレッドの実行が再開される前に待機されます。

モジュールカスタマイズコード

// path-to-my-hooks.js

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

呼び出し側コード

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');
});
port1.unref();

register('./path-to-my-hooks.js', {
  parentURL: import.meta.url,
  data: { number: 1, port: port2 },
  transferList: [port2],
});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');
});
port1.unref();

register('./path-to-my-hooks.js', {
  parentURL: pathToFileURL(__filename),
  data: { number: 1, port: port2 },
  transferList: [port2],
});
resolve(specifier, context, nextResolve)#
  • specifier <string>
  • context <Object>
    • conditions <string[]> 関連する package.json のエクスポート条件
    • importAttributes <Object> インポートするモジュールの属性を表すキーと値のペアを持つオブジェクト
    • parentURL <string> | <undefined> このモジュールをインポートしているモジュール、またはこれが Node.js のエントリーポイントである場合は undefined
  • nextResolve <Function> チェーン内の後続の resolve フック、または最後のユーザー提供の resolve フックの後の Node.js のデフォルトの resolve フック
    • specifier <string>
    • context <Object> | <undefined> 省略された場合、デフォルトが提供されます。提供された場合、デフォルトは提供されたプロパティを優先してマージされます。
  • 戻り値: <Object> | <Promise> 非同期版は、以下のプロパティを含むオブジェクト、またはそのようなオブジェクトに解決される Promise のいずれかを取ります。同期版は、同期的に返されるオブジェクトのみを受け入れます。
    • format <string> | <null> | <undefined> load フックへのヒント (無視される可能性があります)。モジュールフォーマット ('commonjs''module' など) や、'css''yaml' のような任意の値が可能です。
    • importAttributes <Object> | <undefined> モジュールをキャッシュする際に使用するインポート属性 (オプション。除外された場合は入力が使用されます)
    • shortCircuit <undefined> | <boolean> このフックが resolve フックのチェーンを終了させる意図があることを示すシグナル。デフォルト: false
    • url <string> この入力が解決される絶対 URL

警告 非同期版の場合、Promise と async 関数の返却をサポートしているにもかかわらず、resolve への呼び出しは依然としてメインスレッドをブロックする可能性があり、パフォーマンスに影響を与えることがあります。

resolve フックチェーンは、与えられた import 文や式、または require 呼び出しをどこで見つけ、どのようにキャッシュするかを Node.js に伝える責任があります。オプションで、load フックへのヒントとしてフォーマット ('module' など) を返すことができます。フォーマットが指定された場合、最終的な format の値を提供する責任は最終的に load フックにあり (そして resolve によって提供されたヒントを自由に無視できます)、resolveformat を提供する場合、たとえその値を Node.js のデフォルトの load フックに渡すだけであっても、カスタムの load フックが必要になります。

インポートタイプ属性は、読み込まれたモジュールを内部モジュールキャッシュに保存するためのキャッシュキーの一部です。resolve フックは、ソースコードに存在したものとは異なる属性でモジュールをキャッシュすべき場合に、importAttributes オブジェクトを返す責任があります。

contextconditions プロパティは、この解決リクエストの パッケージエクスポート条件に一致させるために使用される条件の配列です。これらは、他の場所で条件付きマッピングを検索したり、デフォルトの解決ロジックを呼び出す際にリストを変更したりするために使用できます。

現在の パッケージエクスポート条件は、常にフックに渡される context.conditions 配列の中にあります。defaultResolve を呼び出す際にデフォルトの Node.js モジュール指定子解決動作を保証するためには、それに渡される context.conditions 配列は、元々 resolve フックに渡された context.conditions 配列のすべての要素を含んでいなければなりません

// Asynchronous version accepted by module.register().
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);
} 
// Synchronous version accepted by module.registerHooks().
function resolve(specifier, context, nextResolve) {
  // Similar to the asynchronous resolve() above, since that one does not have
  // any asynchronous logic.
} 
load(url, context, nextLoad)#
  • url <string> resolve チェーンによって返された URL
  • context <Object>
    • conditions <string[]> 関連する package.json のエクスポート条件
    • format <string> | <null> | <undefined> resolve フックチェーンによってオプションで提供されたフォーマット。これは入力として任意の文字列値を取ることができます。入力値は、以下で説明する許容される戻り値のリストに準拠する必要はありません。
    • importAttributes <Object>
  • nextLoad <Function> チェーン内の後続の load フック、または最後のユーザー提供の load フックの後の Node.js のデフォルトの load フック
    • url <string>
    • context <Object> | <undefined> 省略された場合、デフォルトが提供されます。提供された場合、デフォルトは提供されたプロパティを優先してマージされます。デフォルトの nextLoad では、url が指すモジュールに明示的なモジュールタイプ情報がない場合、context.format は必須です。
  • 戻り値: <Object> | <Promise> 非同期版は、以下のプロパティを含むオブジェクト、またはそのようなオブジェクトに解決される Promise のいずれかを取ります。同期版は、同期的に返されるオブジェクトのみを受け入れます。

load フックは、URL をどのように解釈し、取得し、解析するかを決定するカスタムメソッドを定義する方法を提供します。また、インポート属性の検証も担当します。

format の最終的な値は、以下のいずれかでなければなりません。

フォーマット説明load によって返される source に許容される型
'addon'Node.js アドオンを読み込む<null>
'builtin'Node.js の組み込みモジュールを読み込む<null>
'commonjs-typescript'TypeScript 構文の Node.js CommonJS モジュールを読み込む<string> | <ArrayBuffer> | <TypedArray> | <null> | <undefined>
'commonjs'Node.js CommonJS モジュールを読み込む<string> | <ArrayBuffer> | <TypedArray> | <null> | <undefined>
'json'JSON ファイルを読み込む<string> | <ArrayBuffer> | <TypedArray>
'module-typescript'TypeScript 構文の ES モジュールを読み込む<string> | <ArrayBuffer> | <TypedArray>
'module'ES モジュールを読み込む<string> | <ArrayBuffer> | <TypedArray>
'wasm'WebAssembly モジュールを読み込む<ArrayBuffer> | <TypedArray>

現在 Node.js の組み込み (コア) モジュールの値を置き換えることはできないため、'builtin' 型では source の値は無視されます。

非同期の load フックにおける注意点#

非同期の load フックを使用する場合、'commonjs' に対して source を省略するか提供するかで、効果が大きく異なります。

  • source が提供された場合、このモジュールからのすべての require 呼び出しは、登録された resolve および load フックを持つ ESM ローダーによって処理されます。このモジュールからのすべての require.resolve 呼び出しは、登録された resolve フックを持つ ESM ローダーによって処理されます。CommonJS API のサブセットのみが利用可能になり (例: require.extensionsrequire.cacherequire.resolve.paths はなし)、CommonJS モジュールローダーへのモンキーパッチは適用されません。
  • source が undefined または null の場合、それは CommonJS モジュールローダーによって処理され、require/require.resolve の呼び出しは登録されたフックを通過しません。nullish な source に対するこの動作は一時的なものです — 将来的に、nullish な source はサポートされなくなります。

これらの注意点は、同期の load フックには適用されません。その場合、カスタマイズされた CommonJS モジュールで利用可能な CommonJS API の完全なセットが提供され、require/require.resolve は常に登録されたフックを通過します。

Node.js の内部非同期 load 実装 (load チェーンの最後のフックの next の値) は、後方互換性のために format'commonjs' の場合に sourcenull を返します。以下は、非デフォルトの動作をオプトインするフックの例です。

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

// Asynchronous version accepted by module.register(). This fix is not needed
// for the synchronous version accepted by module.registerHooks().
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 には、モジュールのフォーマットに関係なく、次のフックによって読み込まれたソースコードが含まれます。

警告: 非同期の load フックと CommonJS モジュールからの名前空間付きエクスポートは互換性がありません。これらを一緒に使用しようとすると、インポートから空のオブジェクトが返されます。これは将来的に対処される可能性があります。これは同期の load フックには適用されず、その場合はエクスポートは通常どおり使用できます。

これらの型はすべて ECMAScript で定義されたクラスに対応しています。

テキストベースのフォーマット (つまり 'json''module') のソース値が文字列でない場合、util.TextDecoder を使用して文字列に変換されます。

load フックは、解決された URL のソースコードを取得するためのカスタムメソッドを定義する方法を提供します。これにより、ローダーはディスクからファイルを読み取るのを回避できる可能性があります。また、認識されないフォーマットをサポートされているフォーマットにマッピングするためにも使用できます。例えば、yamlmodule にマッピングするなどです。

// Asynchronous version accepted by module.register().
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);
} 
// Synchronous version accepted by module.registerHooks().
function load(url, context, nextLoad) {
  // Similar to the asynchronous load() above, since that one does not have
  // any asynchronous logic.
} 

より高度なシナリオでは、サポートされていないソースをサポートされているソースに変換するためにも使用できます (以下の を参照)。

#

さまざまなモジュールカスタマイズフックを組み合わせて使用することで、Node.js のコード読み込みと評価の動作を広範囲にカスタマイズすることができます。

HTTPS からのインポート#

以下のフックは、そのような指定子に対する基本的なサポートを有効にするためのフックを登録します。これは 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);
} 
// main.mjs
import { VERSION } from 'https://coffeescript.dokyumento.jp/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 を実行すると、main.mjs 内の URL にあるモジュールに従って、現在のバージョンの CoffeeScript が出力されます。

トランスピレーション#

Node.js が理解できないフォーマットのソースは、load フックを使用して JavaScript に変換できます。

これは、Node.js を実行する前にソースファイルをトランスパイルするよりもパフォーマンスが劣ります。トランスパイラフックは、開発およびテスト目的でのみ使用すべきです。

非同期版#
// coffeescript-hooks.mjs
import { readFile } from 'node:fs/promises';
import { findPackageJSON } from 'node:module';
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. Use a custom format
    // to tell Node.js not to detect its module type.
    const { source: rawSource } = await nextLoad(url, { ...context, format: 'coffee' });
    // This hook converts CoffeeScript source code into JavaScript source code
    // for all imported CoffeeScript files.
    const transformedSource = coffeescript.compile(rawSource.toString(), url);

    // To determine how Node.js would interpret the transpilation result,
    // search up the file system for the nearest parent package.json file
    // and read its "type" field.
    return {
      format: await getPackageType(url),
      shortCircuit: true,
      source: transformedSource,
    };
  }

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

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 pJson = findPackageJSON(url);

  return readFile(pJson, 'utf8')
    .then(JSON.parse)
    .then((json) => json?.type)
    .catch(() => undefined);
} 
同期版#
// coffeescript-sync-hooks.mjs
import { readFileSync } from 'node:fs';
import { registerHooks, findPackageJSON } from 'node:module';
import coffeescript from 'coffeescript';

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

function load(url, context, nextLoad) {
  if (extensionsRegex.test(url)) {
    const { source: rawSource } = nextLoad(url, { ...context, format: 'coffee' });
    const transformedSource = coffeescript.compile(rawSource.toString(), url);

    return {
      format: getPackageType(url),
      shortCircuit: true,
      source: transformedSource,
    };
  }

  return nextLoad(url, context);
}

function getPackageType(url) {
  const pJson = findPackageJSON(url);
  if (!pJson) {
    return undefined;
  }
  try {
    const file = readFileSync(pJson, 'utf-8');
    return JSON.parse(file)?.type;
  } catch {
    return undefined;
  }
}

registerHooks({ load }); 
フックの実行#
# 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}" 
# scream.coffee
export scream = (str) -> str.toUpperCase() 

この例を実行するために、CoffeeScript ファイルのモジュールタイプを含む package.json ファイルを追加します。

{
  "type": "module"
} 

これはあくまで例を実行するためです。実際のローダーでは、package.json に明示的なタイプがない場合でも、getPackageType() は Node.js が認識する format を返すことができなければなりません。そうしないと、nextLoad 呼び出しは ERR_UNKNOWN_FILE_EXTENSION (undefined の場合) または ERR_UNKNOWN_MODULE_FORMAT (load フックのドキュメントに記載されている既知のフォーマットでない場合) をスローします。

前述のフックモジュールを使用して 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 はディスクからソースコードが読み込まれた後、Node.js がそれを実行する前に JavaScript に変換されます。そして、読み込まれたファイルの import 文を介して参照されるすべての .coffee.litcoffee、または .coffee.md ファイルについても同様です。

インポートマップ#

前の 2 つの例では load フックを定義しました。これは resolve フックの例です。このフックモジュールは、どの指定子を他の URL に上書きするかを定義する import-map.json ファイルを読み込みます (これは「インポートマップ」仕様の小さなサブセットの非常に単純な実装です)。

非同期版#
// 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);
} 
同期版#
// 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 }); 
フックの使用#

これらのファイルで

// main.js
import 'a-module'; 
// import-map.json
{
  "imports": {
    "a-module": "./some-module.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! と表示されるはずです。

ソースマップのサポート#

安定性: 1 - Experimental

Node.js は TC39 ECMA-426 ソースマップフォーマット (以前はソースマップリビジョン 3 フォーマットと呼ばれていました) をサポートしています。

このセクションの API は、ソースマップキャッシュと対話するためのヘルパーです。このキャッシュは、ソースマップの解析が有効になっており、モジュールのフッターに ソースマップインクルードディレクティブが見つかった場合に設定されます。

ソースマップの解析を有効にするには、Node.js を --enable-source-maps フラグ付きで実行するか、NODE_V8_COVERAGE=dir を設定してコードカバレッジを有効にするか、module.setSourceMapsSupport() を介してプログラム的に有効にする必要があります。

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

module.getSourceMapsSupport()#

  • 戻り値: <Object>
    • enabled <boolean> ソースマップサポートが有効な場合
    • nodeModules <boolean> サポートが node_modules 内のファイルに対して有効な場合。
    • generatedCode <boolean> サポートが eval または new Function から生成されたコードに対して有効な場合。

このメソッドは、スタックトレースに対する ソースマップ v3 のサポートが有効かどうかを返します。

module.findSourceMap(path)#

path は、対応するソースマップを取得すべきファイルの解決済みパスです。

module.setSourceMapsSupport(enabled[, options])#

  • enabled <boolean> ソースマップサポートを有効にします。
  • options <Object> オプション
    • nodeModules <boolean> node_modules 内のファイルに対するサポートを有効にする場合。デフォルト: false
    • generatedCode <boolean> eval または new Function から生成されたコードに対するサポートを有効にする場合。デフォルト: false

この関数は、スタックトレースに対する ソースマップ v3 サポートを有効または無効にします。

これは、コマンドラインオプション --enable-source-maps で Node.js プロセスを起動するのと同じ機能を提供し、さらに node_modules 内のファイルや生成されたコードに対するサポートを変更するための追加オプションも提供します。

ソースマップが有効になった後に読み込まれた JavaScript ファイル内のソースマップのみが解析され、読み込まれます。この API 呼び出しの前に読み込まれたモジュールのソースマップの追跡を失わないように、コマンドラインオプション --enable-source-maps を使用することが望ましいです。

クラス: module.SourceMap#

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

新しい sourceMap インスタンスを作成します。

payload は、ソースマップフォーマットに一致するキーを持つオブジェクトです。

lineLengths は、生成されたコード内の各行の長さを格納したオプションの配列です。

sourceMap.payload#

SourceMap インスタンスを構築するために使用されたペイロードのゲッター。

sourceMap.findEntry(lineOffset, columnOffset)#
  • lineOffset <number> 生成されたソース内の 0 から始まる行番号オフセット
  • columnOffset <number> 生成されたソース内の 0 から始まる列番号オフセット
  • 戻り値: <Object>

生成されたソースファイル内の行オフセットと列オフセットを指定すると、見つかった場合は元のファイル内の SourceMap 範囲を表すオブジェクトを、見つからなかった場合は空のオブジェクトを返します。

返されるオブジェクトには、以下のキーが含まれます。

  • generatedLine <number> 生成されたソース内の範囲の開始位置の行オフセット
  • generatedColumn <number> 生成されたソース内の範囲の開始位置の列オフセット
  • originalSource <string> ソースマップで報告されている、元のソースのファイル名
  • originalLine <number> 元のソース内の範囲の開始位置の行オフセット
  • originalColumn <number> 元のソース内の範囲の開始位置の列オフセット
  • name <string>

返される値は、エラーメッセージや CallSite オブジェクトに表示される 1 から始まる行番号と列番号ではなく、0 から始まるオフセットに基づいた、ソースマップに現れるままの生の範囲を表します。

エラー スタックや CallSite オブジェクトによって報告される lineNumber と columnNumber から対応する 1 から始まる行番号と列番号を取得するには、sourceMap.findOrigin(lineNumber, columnNumber) を使用します。

sourceMap.findOrigin(lineNumber, columnNumber)#
  • lineNumber <number> 生成されたソース内のコールサイトの 1 から始まる行番号
  • columnNumber <number> 生成されたソース内のコールサイトの 1 から始まる列番号
  • 戻り値: <Object>

生成されたソースのコールサイトから 1 から始まる lineNumbercolumnNumber を指定して、元のソース内の対応するコールサイトの場所を見つけます。

提供された lineNumbercolumnNumber がどのソースマップにも見つからない場合は、空のオブジェクトが返されます。それ以外の場合、返されるオブジェクトには次のキーが含まれます。

  • name <string> | <undefined> ソースマップ内の範囲の名前 (提供されている場合)
  • fileName <string> ソースマップで報告されている、元のソースのファイル名
  • lineNumber <number> 元のソース内の対応するコールサイトの 1 から始まる行番号
  • columnNumber <number> 元のソース内の対応するコールサイトの 1 から始まる列番号