Skip to content

モジュール: node:module API

追加: 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 モジュールをrequireします。

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 を持つモジュールの識別子。 Bare specifier を渡すと、パッケージのルートにある package.json が返されます。 相対 specifier または 絶対 specifier を渡すと、最も近い親の package.json が返されます。
  • base <string> | <URL> 含まれているモジュールの絶対位置(file: URL 文字列または FS パス)。 CJS の場合は、__filename を使用します (__dirname ではありません)。 ESM の場合は、import.meta.url を使用します。 specifier絶対 specifier の場合は、渡す必要はありません。
  • 戻り値: <string> | <undefined> package.json が見つかった場合はパス。 startLocation がパッケージの場合、パッケージのルート package.json。 相対または未解決の場合、startLocation に最も近い package.json
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'
// 代わりに絶対 specifier を渡した場合も同じ結果:
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'
// 絶対 specifier を渡すと、解決されたモジュールがネストされた `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'
// 代わりに絶対 specifier を渡した場合も同じ結果:
findPackageJSON(pathToFileURL(path.join(__dirname, '..')));

findPackageJSON('some-package', __filename);
// '/path/to/project/packages/bar/node_modules/some-package/package.json'
// 絶対 specifier を渡すと、解決されたモジュールがネストされた `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)

Added in: 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])

[History]

VersionChanges
v20.8.0, v18.19.0WHATWG URL インスタンスのサポートを追加。
v20.6.0, v18.19.0Added in: v20.6.0, v18.19.0

[Stable: 1 - Experimental]

Stable: 1 安定度: 1.2 - リリース候補

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

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

module.registerHooks(options)

Added in: v23.5.0

[Stable: 1 - Experimental]

Stable: 1 Stability: 1.1 - 活発な開発

Node.js のモジュール解決とロードの動作をカスタマイズする hooks を登録します。Customization hooks を参照してください。

module.stripTypeScriptTypes(code[, options])

Added in: v23.2.0

[Stable: 1 - Experimental]

Stable: 1 Stability: 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 コードから型アノテーションを削除します。これは、vm.runInContext() または vm.compileFunction() で実行する前に、TypeScript コードから型アノテーションを削除するために使用できます。 デフォルトでは、コードに Enums などの変換が必要な TypeScript 機能が含まれている場合、エラーがスローされます。詳細については、type-stripping を参照してください。 mode が 'transform' の場合、TypeScript の機能を JavaScript に変換します。詳細については、transform TypeScript features を参照してください。 mode が 'strip' の場合、場所が保持されるため、ソースマップは生成されません。 sourceMap が指定されている場合、mode が 'strip' の場合、エラーがスローされます。

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

js
import { stripTypeScriptTypes } from 'node:module';
const code = 'const a: number = 1;';
const strippedCode = stripTypeScriptTypes(code);
console.log(strippedCode);
// Prints: const a         = 1;
js
const { stripTypeScriptTypes } = require('node:module');
const code = 'const a: number = 1;';
const strippedCode = stripTypeScriptTypes(code);
console.log(strippedCode);
// Prints: 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);
// Prints: 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);
// Prints: 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);
// Prints:
// 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);
// Prints:
// 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 Modules のすべてのライブバインディングを更新して、CommonJS エクスポートのプロパティと一致させます。ES Modules からエクスポートされた名前を追加または削除しません。

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() は esmFS から readFileSync を削除しません
  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() を使用します。

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

有効になっているモジュールコンパイルキャッシュは、NODE_DISABLE_COMPILE_CACHE=1 環境変数で無効にできます。これは、コンパイルキャッシュが予期しない動作または望ましくない動作(たとえば、テストカバレッジの精度が低い)につながる場合に役立ちます。

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

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

module.constants.compileCacheStatus

Added in: v22.8.0

[Stable: 1 - Experimental]

Stable: 1 Stability: 1.1 - 活発な開発

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

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

module.enableCompileCache([cacheDir])

Added in: v22.8.0

[Stable: 1 - Experimental]

Stable: 1 Stability: 1.1 - 活発な開発

  • cacheDir <string> | <undefined> コンパイルキャッシュが保存/取得されるディレクトリを指定するオプションのパス。
  • 戻り値: <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インスタンスでモジュールコンパイルキャッシュを有効にします。

cacheDirが指定されていない場合、Node.jsは、NODE_COMPILE_CACHE=dir環境変数が設定されている場合はそれによって指定されたディレクトリを使用し、それ以外の場合はpath.join(os.tmpdir(), 'node-compile-cache')を使用します。一般的なユースケースでは、cacheDirを指定せずにmodule.enableCompileCache()を呼び出すことをお勧めします。これにより、必要に応じてNODE_COMPILE_CACHE環境変数でディレクトリをオーバーライドできます。

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

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

module.flushCompileCache()

Added in: v23.0.0

[Stable: 1 - Experimental]

Stable: 1 安定性: 1.1 - 活発な開発

現在の Node.js インスタンスに既にロードされているモジュールから蓄積されたモジュールコンパイルキャッシュをディスクにフラッシュします。これは、フラッシュファイルシステム操作が成功するかどうかにかかわらず、すべて終了した後に返されます。エラーが発生した場合、コンパイルキャッシュのミスはアプリケーションの実際の動作を妨げるべきではないため、これは静かに失敗します。

module.getCompileCacheDir()

Added in: v22.8.0

[Stable: 1 - Experimental]

Stable: 1 安定性: 1.1 - 活発な開発

カスタマイズフック

[履歴]

バージョン変更
v23.5.0同期およびインスレッドフックのサポートを追加。
v20.6.0, v18.19.0globalPreload を置き換える initialize フックを追加。
v18.6.0, v16.17.0ローダーのチェーンのサポートを追加。
v16.12.0getFormatgetSourcetransformSource、および globalPreload を削除。load フックと getGlobalPreload フックを追加。
v8.8.0Added in: v8.8.0

[Stable: 1 - Experimental]

Stable: 1 安定性: 1.2 - リリース候補 (非同期バージョン) 安定性: 1.1 - 活発な開発 (同期バージョン)

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

有効化

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

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

bash
node --import ./register-hooks.js ./my-app.js
node --require ./register-hooks.js ./my-app.js
js
// register-hooks.js
// このファイルは、トップレベルの await が含まれていない場合にのみ require() できます。
// 専用のスレッドで非同期フックを登録するには、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 には、次の register-hooks.js の例のように、register() を呼び出すファイルにマッピングされる /register エクスポートを定義する "exports" フィールドがあります。

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

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

js
import { register } from 'node:module';

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

// これは動的な `import()` であるため、`http-to-https` フックが実行されて
// `./my-app.js` およびそれがインポートまたは require する他のすべてのファイルを処理します。
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` およびそれがインポートまたは require する他のすべてのファイルを処理します。
import('./my-app.js');

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

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

import('./my-app.js') が代わりに静的な import './my-app.js' であった場合、http-to-https フックが登録される前にアプリケーションは既にロードされていたでしょう。これは、静的なインポートが最初にツリーの葉から評価され、次に幹に戻るという ES モジュール仕様によるものです。my-app.js 内に静的なインポートが存在する可能性があり、これらは my-app.js が動的にインポートされるまで評価されません。

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

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

registerHooks({ /* 同期フックの実装 */ });

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

// 同期フックは、import、require()、および createRequire() を介して作成されたユーザー require() 関数に影響を与えます。
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()、および createRequire() を介して作成されたユーザー require() 関数に影響を与えます。
import('./my-app.js');
require('./my-app-2.js');
userRequire('./my-app-3.js');

最後に、アプリケーションを実行する前にフックを登録するだけでよく、そのために別のファイルを作成したくない場合は、data: URL を --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 } from 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 のチェーン化も同様に機能します。同期フックと非同期フックが混在している場合、同期フックは常に非同期フックが実行される前に最初に実行されます。つまり、最後に実行される同期フックでは、次のフックに非同期フックの呼び出しが含まれます。

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` の specifier を受け取り、URL に解決します。
}

export async function load(url, context, nextLoad) {
  // 解決された URL を受け取り、評価されるソースコードを返します。
}

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

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

追加: v23.5.0

[安定版: 1 - 試験的]

安定版: 1 安定性: 1.1 - 活発な開発

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

js
function resolve(specifier, context, nextResolve) {
  // `import` または `require` の specifier を受け取り、URL に解決します。
}

function load(url, context, nextLoad) {
  // 解決された URL を受け取り、評価されるソースコードを返します。
}

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

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

フックの規約

フックは チェーン の一部です。たとえそのチェーンが 1 つのカスタム(ユーザー提供)フックと常に存在するデフォルトフックのみで構成されていたとしてもです。フック関数はネストされます。それぞれの関数は常にプレーンなオブジェクトを返し、チェインは各関数が 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)

[History]

VersionChanges
v23.5.0同期およびインスレッドフックのサポートを追加しました。
v21.0.0, v20.10.0, v18.19.0プロパティcontext.importAssertionscontext.importAttributesに置き換えられました。古い名前の使用も引き続きサポートされ、実験的な警告が表示されます。
v18.6.0, v16.17.0resolveフックの連鎖のサポートを追加しました。各フックは、nextResolve()を呼び出すか、返り値にshortCircuitプロパティをtrueに設定する必要があります。
v17.1.0, v16.14.0import assertionsのサポートを追加しました。

[Stable: 1 - Experimental]

Stable: 1 Stability: 1.2 - リリース候補(非同期バージョン)Stability: 1.1 - アクティブな開発(同期バージョン)

  • specifier <string>

  • context <Object>

    • conditions <string[]> 関連するpackage.jsonのエクスポート条件
    • importAttributes <Object> インポートするモジュールの属性を表すキーと値のペアのオブジェクト
    • parentURL <string> | <undefined> これをインポートするモジュール。Node.jsのエントリーポイントの場合はundefined
  • nextResolve <Function> チェーン内の後続のresolveフック、または最後のユーザー指定のresolveフックの後のNode.jsのデフォルトのresolveフック

  • Returns: <Object> | <Promise> 非同期バージョンは、次のプロパティを含むオブジェクト、またはそのようなオブジェクトに解決されるPromiseのいずれかを受け取ります。同期バージョンは、同期的に返されるオブジェクトのみを受け入れます。

    • format <string> | <null> | <undefined> loadフックへのヒント(無視される可能性があります) 'builtin' | 'commonjs' | 'json' | 'module' | 'wasm'
    • importAttributes <Object> | <undefined> モジュールをキャッシュするときに使用するインポート属性(オプション; 除外された場合、入力が使用されます)
    • shortCircuit <undefined> | <boolean> このフックがresolveフックのチェーンを終了させる意図があるというシグナル。 Default: false
    • url <string> この入力が解決される絶対URL

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

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

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

現在のpackage exports conditionsは、常にフックに渡されるcontext.conditions配列にあります。defaultResolveを呼び出すときにデフォルトのNode.jsモジュール指定子解決動作を保証するには、それに渡されるcontext.conditions配列に、元々resolveフックに渡されたcontext.conditions配列のすべての要素が含まれている必要があります

js
// module.register() で受け入れられる非同期バージョン。
export async function resolve(specifier, context, nextResolve) {
  const { parentURL = null } = context;

  if (Math.random() > 0.5) { // 何らかの条件。
    // 一部のまたはすべての指定子について、解決するためのカスタムロジックを実行します。
    // 常に {url: <string>} 形式のオブジェクトを返します。
    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.0commonjs形式のsourceのサポートを追加。
v18.6.0, v16.17.0loadフックのチェーンのサポートを追加。各フックは、nextLoad()を呼び出すか、返り値にshortCircuitプロパティをtrueに設定して含める必要があります。

[安定版: 1 - 実験的]

安定版: 1 安定度: 1.2 - リリース候補 (非同期バージョン) 安定度: 1.1 - アクティブ開発 (同期バージョン)

  • url <string> resolveチェーンによって返されるURL

  • context <Object>

    • conditions <string[]> 関連するpackage.jsonのエクスポート条件
    • format <string> | <null> | <undefined> resolveフックチェーンによってオプションで提供される形式
    • importAttributes <Object>
  • nextLoad <Function> チェーン内の後続のloadフック、または最後のユーザー提供のloadフック後のNode.jsのデフォルトloadフック

  • 戻り値: <Object> | <Promise> 非同期バージョンは、以下のプロパティを含むオブジェクト、またはそのようなオブジェクトに解決されるPromiseのいずれかを受け取ります。同期バージョンは、同期的に返されたオブジェクトのみを受け入れます。

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

formatの最終的な値は、次のいずれかである必要があります。

format説明loadによって返されるsourceに対して許容される型
'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組み込み(コア)モジュールの値を置き換えることができないためです。
非同期 load フックにおける注意点

非同期 load フックを使用する場合、'commonjs' に対して source を省略するか提供するかで、非常に異なる影響があります。

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

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

Node.js の内部非同期 load 実装は、load チェーンの最後のフックの next の値であり、下位互換性のために format'commonjs' の場合、source に対して null を返します。非デフォルトの動作を使用することを選択するフックの例を以下に示します。

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 には、モジュール形式に関係なく、次のフックによってロードされたソースコードが含まれます。

テキストベースの形式(つまり、'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: <string>,
        source: <string|buffer>,
      }.
    */
    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));
    });
  }

  // 他のすべての URL は Node.js に処理させます。
  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 を実行すると、main.mjs の URL にあるモジュールごとの CoffeeScript の現在のバージョンが出力されます。

トランスパイル

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

これは、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ファイルが、同じ場所にある.jsファイルと同じようにNode.jsによって
    // 扱われるようにする必要があります。 Node.jsが任意の.jsファイルをどのように解釈するかを
    // 判断するには、ファイルシステムを検索して、最も近い親package.jsonファイルを見つけ、
    // その「type」フィールドを読み取ります。
    const format = await getPackageType(url);

    const { source: rawSource } = await nextLoad(url, { ...context, format });
    // このフックは、インポートされたすべてのCoffeeScriptファイルのCoffeeScriptソースコードを
    // JavaScriptソースコードに変換します。
    const transformedSource = coffeescript.compile(rawSource.toString(), url);

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

  // 他のすべてのURLはNode.jsに処理させます。
  return nextLoad(url);
}

async function getPackageType(url) {
  // `url`は、load()フックから解決されたurlを渡された最初のイテレーションの間のみ、ファイルパスです
  // 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, '..'));
}
同期バージョン
js
// 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 });

フックの実行

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

import { version } from 'node:process'
console.log "Node.js バージョン #{version} がお届けします"
coffee
# 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 は、そのソースコードがディスクからロードされた後、Node.js がそれを実行する前に JavaScript に変換されます。また、ロードされたファイルの import ステートメントを介して参照される .coffee.litcoffee、または .coffee.md ファイルについても同様です。

インポートマップ

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

非同期バージョン
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 サポート

Added in: v13.7.0, v12.17.0

[Stable: 1 - Experimental]

Stable: 1 Stability: 1 - Experimental

ソースマップキャッシュとやり取りするためのヘルパー。このキャッシュは、ソースマップ解析が有効になっていて、モジュールのフッターにソースマップインクルードディレクティブが見つかった場合に作成されます。

ソースマップ解析を有効にするには、Node.jsをフラグ--enable-source-maps、またはNODE_V8_COVERAGE=dirを設定してコードカバレッジを有効にして実行する必要があります。

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

module.findSourceMap(path)

追加: v13.7.0, v12.17.0

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

Class: module.SourceMap

追加: v13.7.0, v12.17.0

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

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

payload は、Source map 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>

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

Errorスタックと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> SourceMap で報告されている元のソースのファイル名
  • lineNumber: <number> 元のソースの対応する呼び出し元の 1 から始まる lineNumber
  • columnNumber: <number> 元のソースの対応する呼び出し元の 1 から始まる columnNumber