Skip to content

Modules: 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 のモジュール指定子。ベア指定子 を渡すと、パッケージのルートにある package.json が返されます。相対指定子 または 絶対指定子 を渡すと、最も近い親の package.json が返されます。
  • base <string> | <URL> 包含モジュールの絶対位置 (file: URL 文字列または FS パス)。CJS の場合は __filename ( __dirname ではない!)を使用します。ESM の場合は import.meta.url を使用します。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'
// 代わりに絶対指定子を渡した場合も同じ結果になります。
findPackageJSON(new URL('../', import.meta.url))
findPackageJSON(import.meta.resolve('../'))

findPackageJSON('some-package', import.meta.url)
// '/path/to/project/packages/bar/node_modules/some-package/package.json'
// 絶対指定子を渡すと、解決されたモジュールがネストされた `package.json` を持つサブフォルダー内にある場合、異なる結果が得られる可能性があります。
findPackageJSON(import.meta.resolve('some-package'))
// '/path/to/project/packages/bar/node_modules/some-package/some-subfolder/package.json'

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

findPackageJSON('..', __filename)
// '/path/to/project/package.json'
// 代わりに絶対指定子を渡した場合も同じ結果になります。
findPackageJSON(pathToFileURL(path.join(__dirname, '..')))

findPackageJSON('some-package', __filename)
// '/path/to/project/packages/bar/node_modules/some-package/package.json'
// 絶対指定子を渡すと、解決されたモジュールがネストされた `package.json` を持つサブフォルダー内にある場合、異なる結果が得られる可能性があります。
findPackageJSON(pathToFileURL(require.resolve('some-package')))
// '/path/to/project/packages/bar/node_modules/some-package/some-subfolder/package.json'

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

module.isBuiltin(moduleName)

追加: v18.6.0, v16.17.0

  • moduleName <string> モジュールの名前
  • 戻り値: <boolean> モジュールが組み込みであれば true を返し、そうでなければ false を返します
js
import { isBuiltin } from 'node:module'
isBuiltin('node:fs') // true
isBuiltin('fs') // true
isBuiltin('wss') // false

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

[履歴]

バージョン変更点
v20.8.0, v18.19.0WHATWG URL インスタンスのサポートを追加。
v20.6.0, v18.19.0追加: v20.6.0, v18.19.0

[安定版: 1 - 試験的]

安定版: 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)

追加: v23.5.0

[安定版: 1 - 試験的]

安定版: 1 安定版: 1.1 - 積極的な開発

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

module.stripTypeScriptTypes(code[, options])

追加: v23.2.0

[安定版: 1 - 試験的]

安定版: 1 安定版: 1.1 - 積極的な開発

  • code <string> 型注釈を削除するコード。

  • options <Object>

    • mode <string> デフォルト: 'strip'。使用可能な値は次のとおりです。

    • 'strip' TypeScript の機能を変換せずに、型注釈のみを削除します。

    • 'transform' 型注釈を削除し、TypeScript の機能を JavaScript に変換します。

    • sourceMap <boolean> デフォルト: falsemode'transform' の場合に限り、true の場合、変換されたコードに対してソースマップが生成されます。

    • sourceUrl <string> ソースマップで使用されるソース URL を指定します。

  • 戻り値: <string> 型注釈が削除されたコード。module.stripTypeScriptTypes() は、TypeScript コードから型注釈を削除します。vm.runInContext() または vm.compileFunction() で実行する前に、TypeScript コードから型注釈を削除するために使用できます。デフォルトでは、コードに Enums などの変換を必要とする TypeScript 機能が含まれている場合、エラーがスローされます。詳細については、type-stripping を参照してください。mode が 'transform' の場合、TypeScript の機能も JavaScript に変換されます。詳細については、TypeScript 機能の変換 を参照してください。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)
// 出力: const a         = 1;
js
const { stripTypeScriptTypes } = require('node:module')
const code = 'const a: number = 1;'
const strippedCode = stripTypeScriptTypes(code)
console.log(strippedCode)
// 出力: const a         = 1;

sourceUrl が提供されている場合、出力の末尾にコメントとして追加されます。

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

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

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

module.syncBuiltinESMExports()

追加: v12.12.0

module.syncBuiltinESMExports() メソッドは、組み込みの ES 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

追加: v22.8.0

[安定版: 1 - 実験的]

安定版: 1 安定版: 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])

追加: v22.8.0

[安定版: 1 - 実験的]

安定版: 1 安定版: 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()

追加: v23.0.0

[安定版: 1 - 実験的]

安定版: 1 安定版: 1.1 - アクティブ開発

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

module.getCompileCacheDir()

追加: v22.8.0

[安定版: 1 - 実験的]

安定版: 1 安定版: 1.1 - アクティブ開発

カスタマイズフック

[履歴]

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

[安定版: 1 - 実験的]

安定版: 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 } = 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

[Stable: 1 - 実験的]

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

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

フックの規約

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

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

initialize()

Added in: v20.6.0, v18.19.0

[Stable: 1 - Experimental]

Stable: 1 Stability: 1.2 - リリース候補

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

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

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

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

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

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

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

呼び出し側のコード:

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

// この例では、メッセージチャネルを使用して、メイン(アプリケーション)スレッドと
// フックスレッドで実行されているフック間で通信する方法を示しています。
// `port2` を `initialize` フックに送信することで行います。
const { port1, port2 } = new MessageChannel()

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

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

// この例では、メッセージチャネルを使用して、メイン(アプリケーション)スレッドと
// フックスレッドで実行されているフック間で通信する方法を示しています。
// `port2` を `initialize` フックに送信することで行います。
const { port1, port2 } = new MessageChannel()

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

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

resolve(specifier, context, nextResolve)

[履歴]

バージョン変更点
v23.5.0同期およびスレッド内フックのサポートを追加。
v21.0.0, v20.10.0, v18.19.0プロパティcontext.importAssertionscontext.importAttributesに置き換えられました。古い名前を使用することはまだサポートされており、実験的な警告が出力されます。
v18.6.0, v16.17.0resolve フックのチェーンをサポートしました。各フックは、nextResolve()を呼び出すか、またはその戻り値にshortCircuitプロパティをtrueに設定する必要があります。
v17.1.0, v16.14.0インポートアサーションのサポートを追加。

[安定版: 1 - 試験的]

安定版: 1 安定性: 1.2 - リリース候補 (非同期バージョン) 安定性: 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フック

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

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

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

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

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

現在のパッケージエクスポート条件は、常にフックに渡されるcontext.conditions配列に含まれています。defaultResolveを呼び出すときに、デフォルトの Node.js モジュール指定子解決動作を保証するには、resolveフックに最初に渡されたcontext.conditions配列のすべての要素が、それに渡される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 の値は、現在 Node.js の組み込み (コア) モジュールの値を置き換えることができないため、'builtin' 型では無視されます。

非同期 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 呼び出しは登録されたフックを通過しません。nullish の source に対するこの動作は一時的なものであり、将来は nullish の 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')の source 値が文字列でない場合、util.TextDecoder を使用して文字列に変換されます。

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

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 モジュールのいずれかである可能性があるため、
    // Node.js では CoffeeScript ファイルを同じ場所にある .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` にファイル拡張子が含まれているかどうかのこの単純な truthy チェックは、
  // ほとんどのプロジェクトで機能するが、いくつかのエッジケース (拡張子のないファイルや、末尾にスペースがある 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, '..'))
}
Synchronous version
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 })

Running hooks

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

import { version } from 'node:process'
console.log "Brought to you by Node.js version #{version}"
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! が出力されるはずです。

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

追加: v13.7.0, v12.17.0

[安定版: 1 - 試験的]

安定版: 1 安定度: 1 - 試験的

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

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

js
// module.mjs
// ECMAScript モジュール内
import { findSourceMap, SourceMap } from 'node:module'
js
// module.cjs
// CommonJS モジュール内
const { findSourceMap, SourceMap } = require('node:module')

module.findSourceMap(path)

追加: v13.7.0, v12.17.0

  • path <string>
  • 戻り値: <module.SourceMap> | <undefined> ソースマップが見つかった場合は module.SourceMap を返し、それ以外の場合は undefined を返します。

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

クラス: module.SourceMap

追加: v13.7.0, v12.17.0

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

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

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

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

sourceMap.payload

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

sourceMap.findEntry(lineOffset, columnOffset)

  • lineOffset <number> 生成されたソースにおける、ゼロから始まる行番号オフセット
  • columnOffset <number> 生成されたソースにおける、ゼロから始まる列番号オフセット
  • Returns: <Object>

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

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

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

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

エラーのスタックや 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 から始まる列番号