Node.js v21.7.2 ドキュメント
- Node.js v21.7.2
-
► 目次
- VM (JavaScript の実行)
- クラス:
vm.Script
- クラス:
vm.Module
- クラス:
vm.SourceTextModule
- クラス:
vm.SyntheticModule
vm.compileFunction(code[, params[, options]])
vm.constants
vm.createContext([contextObject[, options]])
vm.isContext(object)
vm.measureMemory([options])
vm.runInContext(code, contextifiedObject[, options])
vm.runInNewContext(code[, contextObject[, options]])
vm.runInThisContext(code[, options])
- 例: VM 内での HTTP サーバーの実行
- オブジェクトを「コンテキスト化」するとはどういう意味ですか?
- 非同期タスクと Promise とのタイムアウトのやり取り
- コンパイル API での動的
import()
のサポート
- クラス:
- VM (JavaScript の実行)
-
► インデックス
- アサーションテスト
- 非同期コンテキスト追跡
- Async Hooks
- Buffer
- C++ アドオン
- Node-API を使用した C/C++ アドオン
- C++ エンベッダー API
- 子プロセス
- Cluster
- コマンドラインオプション
- コンソール
- Corepack
- 暗号化
- デバッガー
- 非推奨 API
- Diagnostics Channel
- DNS
- ドメイン
- エラー
- イベント
- ファイルシステム
- グローバル
- HTTP
- HTTP/2
- HTTPS
- インスペクター
- 国際化
- モジュール: CommonJS モジュール
- モジュール: ECMAScript モジュール
- モジュール:
node:module
API - モジュール: パッケージ
- Net
- OS
- Path
- パフォーマンスフック
- パーミッション
- プロセス
- Punycode
- クエリ文字列
- Readline
- REPL
- レポート
- 単一実行可能アプリケーション
- ストリーム
- 文字列デコーダー
- テストランナー
- タイマー
- TLS/SSL
- トレースイベント
- TTY
- UDP/データグラム
- URL
- ユーティリティ
- V8
- VM
- WASI
- Web Crypto API
- Web Streams API
- ワーカー スレッド
- Zlib
- ► その他のバージョン
- ► オプション
VM (JavaScript の実行)#
ソースコード: lib/vm.js
node:vm
モジュールを使用すると、V8 仮想マシン コンテキスト内でコードをコンパイルして実行できます。
node:vm
モジュールはセキュリティ メカニズムではありません。信頼できないコードを実行するために使用しないでください。
JavaScript コードは、すぐにコンパイルして実行するか、コンパイルして保存し、後で実行できます。
一般的なユースケースは、別の V8 コンテキストでコードを実行することです。これは、呼び出されたコードが、呼び出し元のコードとは異なるグローバル オブジェクトを持つことを意味します。
オブジェクトをコンテキスト化することで、コンテキストを提供できます。呼び出されたコードは、コンテキスト内の任意のプロパティをグローバル変数のように扱います。呼び出されたコードによって引き起こされたグローバル変数への変更は、コンテキスト オブジェクトに反映されます。
const vm = require('node:vm');
const x = 1;
const context = { x: 2 };
vm.createContext(context); // Contextify the object.
const code = 'x += 40; var y = 17;';
// `x` and `y` are global variables in the context.
// Initially, x has the value 2 because that is the value of context.x.
vm.runInContext(code, context);
console.log(context.x); // 42
console.log(context.y); // 17
console.log(x); // 1; y is not defined.
クラス: vm.Script
#
vm.Script
クラスのインスタンスには、特定のコンテキストで実行できるプリコンパイルされたスクリプトが含まれています。
new vm.Script(code[, options])
#
code
<string> コンパイルする JavaScript コード。options
<Object> | <string>filename
<string> このスクリプトによって生成されたスタックトレースで使用されるファイル名を指定します。既定値:'evalmachine.<anonymous>'
。lineOffset
<number> このスクリプトによって生成されたスタックトレースに表示される行番号オフセットを指定します。既定値:0
。columnOffset
<number> このスクリプトによって生成されたスタックトレースに表示される 1 行目の列番号オフセットを指定します。既定値:0
。cachedData
<Buffer> | <TypedArray> | <DataView> 指定されたソースの V8 のコードキャッシュデータを持つオプションのBuffer
またはTypedArray
、またはDataView
を提供します。提供された場合、cachedDataRejected
の値は、V8 によるデータの受け入れに応じて、true
またはfalse
のいずれかに設定されます。produceCachedData
<boolean>true
で、cachedData
が存在しない場合、V8 はcode
のコードキャッシュデータの生成を試みます。成功すると、V8 のコードキャッシュデータを持つBuffer
が生成され、返されたvm.Script
インスタンスのcachedData
プロパティに保存されます。cachedDataProduced
の値は、コードキャッシュデータが正常に生成されたかどうかによって、true
またはfalse
のいずれかに設定されます。このオプションは、script.createCachedData()
の代わりに非推奨です。既定値:false
。importModuleDynamically
<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER>import()
が呼び出されたときに、このスクリプトの評価中にモジュールをロードする方法を指定するために使用されます。このオプションは、実験的なモジュール API の一部です。本番環境での使用はお勧めしません。詳細については、コンパイル API での動的import()
のサポート を参照してください。
options
が文字列の場合、ファイル名を指定します。
新しい vm.Script
オブジェクトを作成すると、code
がコンパイルされますが、実行されません。コンパイルされた vm.Script
は、後で複数回実行できます。code
はグローバル オブジェクトにバインドされていません。代わりに、実行ごとに、その実行のためだけにバインドされます。
script.cachedDataRejected
#
cachedData
が vm.Script
の作成時に提供された場合、この値は V8 によるデータの受け入れに応じて true
または false
のいずれかに設定されます。それ以外の場合、値は undefined
です。
script.createCachedData()
#
- 返り値: <Buffer>
Script
コンストラクターの cachedData
オプションで使用できるコードキャッシュを作成します。Buffer
を返します。このメソッドは、いつでも、何度でも呼び出すことができます。
Script
のコードキャッシュには、JavaScript で観測可能な状態は含まれていません。コードキャッシュは、スクリプトソースと一緒に保存し、新しい Script
インスタンスを複数回構築するために使用しても安全です。
Script
ソース内の関数は、遅延コンパイルとしてマークできます。これらの関数は、Script
の構築時にはコンパイルされません。これらの関数は、最初に呼び出されたときにコンパイルされます。コードキャッシュは、V8 が現在 Script
について知っているメタデータをシリアル化し、将来のコンパイルを高速化するために使用できます。
const script = new vm.Script(`
function add(a, b) {
return a + b;
}
const x = add(1, 2);
`);
const cacheWithoutAdd = script.createCachedData();
// In `cacheWithoutAdd` the function `add()` is marked for full compilation
// upon invocation.
script.runInThisContext();
const cacheWithAdd = script.createCachedData();
// `cacheWithAdd` contains fully compiled function `add()`.
script.runInContext(contextifiedObject[, options])
#
contextifiedObject
<Object>vm.createContext()
メソッドによって返されたコンテキスト化されたオブジェクト。options
<Object>displayErrors
<boolean>true
の場合、code
のコンパイル中にError
が発生した場合、エラーの原因となったコード行がスタックトレースに付加されます。既定値:true
。timeout
<integer> 実行を終了する前にcode
を実行するミリ秒数を指定します。実行が終了すると、Error
がスローされます。この値は、厳密な正の整数である必要があります。breakOnSigint
<boolean>true
の場合、SIGINT
(Ctrl+C) を受信すると、実行が終了し、Error
がスローされます。process.on('SIGINT')
を介してアタッチされたイベントの既存のハンドラーは、スクリプトの実行中は無効になりますが、その後も引き続き機能します。既定値:false
。
- 返り値: <any> スクリプトで実行された最後のステートメントの結果。
指定された contextifiedObject
内で vm.Script
オブジェクトに含まれるコンパイル済みコードを実行し、結果を返します。コードを実行しても、ローカル スコープにはアクセスできません。
次の例は、グローバル変数をインクリメントし、別のグローバル変数の値を設定するコードをコンパイルし、そのコードを複数回実行します。グローバル変数は、context
オブジェクトに含まれています。
const vm = require('node:vm');
const context = {
animal: 'cat',
count: 2,
};
const script = new vm.Script('count += 1; name = "kitty";');
vm.createContext(context);
for (let i = 0; i < 10; ++i) {
script.runInContext(context);
}
console.log(context);
// Prints: { animal: 'cat', count: 12, name: 'kitty' }
timeout
または breakOnSigint
オプションを使用すると、新しいイベントループと対応するスレッドが開始され、パフォーマンスに無視できないオーバーヘッドが発生します。
script.runInNewContext([contextObject[, options]])
#
contextObject
<Object> コンテキスト化されるオブジェクト。undefined
の場合、新しいオブジェクトが作成されます。options
<Object>displayErrors
<boolean>true
の場合、code
のコンパイル中にError
が発生した場合、エラーの原因となったコード行がスタックトレースに付加されます。既定値:true
。timeout
<integer> 実行を終了する前にcode
を実行するミリ秒数を指定します。実行が終了すると、Error
がスローされます。この値は、厳密な正の整数である必要があります。breakOnSigint
<boolean>true
の場合、SIGINT
(Ctrl+C) を受信すると、実行が終了し、Error
がスローされます。process.on('SIGINT')
を介してアタッチされたイベントの既存のハンドラーは、スクリプトの実行中は無効になりますが、その後も引き続き機能します。既定値:false
。contextName
<string> 新しく作成されたコンテキストの人間が読める名前。デフォルト:'VM Context i'
。ここで、i
は作成されたコンテキストの昇順の数値インデックスです。contextOrigin
<string> 表示目的で、新しく作成されたコンテキストに対応するオリジン。オリジンはURLのようにフォーマットする必要がありますが、スキーム、ホスト、ポート(必要な場合)のみを含み、url.origin
プロパティの値のように、URL
オブジェクト。最も重要なのは、この文字列は、パスを示す末尾のスラッシュを省略する必要があることです。デフォルト:''
。contextCodeGeneration
<Object>microtaskMode
<string>afterEvaluate
に設定すると、マイクロタスク(Promise
とasync function
によってスケジュールされたタスク)は、スクリプトの実行直後に実行されます。この場合、それらはtimeout
およびbreakOnSigint
のスコープに含まれます。
- 返り値: <any> スクリプトで実行された最後のステートメントの結果。
最初に指定されたcontextObject
をコンテキスト化し、作成されたコンテキスト内でvm.Script
オブジェクトに含まれるコンパイル済みコードを実行し、結果を返します。実行中のコードはローカルスコープにアクセスできません。
次の例では、グローバル変数を設定するコードをコンパイルし、異なるコンテキストで複数回コードを実行します。グローバル変数は、個々のcontext
に設定され、含まれます。
const vm = require('node:vm');
const script = new vm.Script('globalVar = "set"');
const contexts = [{}, {}, {}];
contexts.forEach((context) => {
script.runInNewContext(context);
});
console.log(contexts);
// Prints: [{ globalVar: 'set' }, { globalVar: 'set' }, { globalVar: 'set' }]
script.runInThisContext([options])
#
options
<Object>displayErrors
<boolean>true
の場合、code
のコンパイル中にError
が発生した場合、エラーの原因となったコード行がスタックトレースに付加されます。既定値:true
。timeout
<integer> 実行を終了する前にcode
を実行するミリ秒数を指定します。実行が終了すると、Error
がスローされます。この値は、厳密な正の整数である必要があります。breakOnSigint
<boolean>true
の場合、SIGINT
(Ctrl+C) を受信すると、実行が終了し、Error
がスローされます。process.on('SIGINT')
を介してアタッチされたイベントの既存のハンドラーは、スクリプトの実行中は無効になりますが、その後も引き続き機能します。既定値:false
。
- 返り値: <any> スクリプトで実行された最後のステートメントの結果。
現在のglobal
オブジェクトのコンテキスト内で、vm.Script
に含まれるコンパイル済みコードを実行します。実行中のコードはローカルスコープにアクセスできませんが、現在のglobal
オブジェクトにはアクセスできます。
次の例は、global
変数をインクリメントするコードをコンパイルし、そのコードを複数回実行します。
const vm = require('node:vm');
global.globalVar = 0;
const script = new vm.Script('globalVar += 1', { filename: 'myfile.vm' });
for (let i = 0; i < 1000; ++i) {
script.runInThisContext();
}
console.log(globalVar);
// 1000
script.sourceMapURL
#
スクリプトがソースマップマジックコメントを含むソースからコンパイルされる場合、このプロパティはソースマップのURLに設定されます。
import vm from 'node:vm';
const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`);
console.log(script.sourceMapURL);
// Prints: sourcemap.json
const vm = require('node:vm');
const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`);
console.log(script.sourceMapURL);
// Prints: sourcemap.json
クラス: vm.Module
#
この機能は、--experimental-vm-modules
コマンドフラグが有効になっている場合にのみ使用できます。
vm.Module
クラスは、VMコンテキストでECMAScriptモジュールを使用するための低レベルのインターフェースを提供します。これは、ECMAScript仕様で定義されているモジュールレコードを厳密に反映したvm.Script
クラスの対応物です。
ただし、vm.Script
とは異なり、すべてのvm.Module
オブジェクトは、作成時からコンテキストにバインドされます。vm.Module
オブジェクトの操作は、vm.Script
オブジェクトの同期的な性質とは対照的に、本質的に非同期です。「async」関数を使用すると、vm.Module
オブジェクトの操作に役立ちます。
vm.Module
オブジェクトを使用するには、作成/解析、リンク、および評価の3つの異なるステップが必要です。これらの3つのステップを次の例で説明します。
この実装は、ECMAScriptモジュールローダーよりも低いレベルにあります。また、ローダーとやり取りする方法はまだありませんが、サポートが計画されています。
import vm from 'node:vm';
const contextifiedObject = vm.createContext({
secret: 42,
print: console.log,
});
// Step 1
//
// Create a Module by constructing a new `vm.SourceTextModule` object. This
// parses the provided source text, throwing a `SyntaxError` if anything goes
// wrong. By default, a Module is created in the top context. But here, we
// specify `contextifiedObject` as the context this Module belongs to.
//
// Here, we attempt to obtain the default export from the module "foo", and
// put it into local binding "secret".
const bar = new vm.SourceTextModule(`
import s from 'foo';
s;
print(s);
`, { context: contextifiedObject });
// Step 2
//
// "Link" the imported dependencies of this Module to it.
//
// The provided linking callback (the "linker") accepts two arguments: the
// parent module (`bar` in this case) and the string that is the specifier of
// the imported module. The callback is expected to return a Module that
// corresponds to the provided specifier, with certain requirements documented
// in `module.link()`.
//
// If linking has not started for the returned Module, the same linker
// callback will be called on the returned Module.
//
// Even top-level Modules without dependencies must be explicitly linked. The
// callback provided would never be called, however.
//
// The link() method returns a Promise that will be resolved when all the
// Promises returned by the linker resolve.
//
// Note: This is a contrived example in that the linker function creates a new
// "foo" module every time it is called. In a full-fledged module system, a
// cache would probably be used to avoid duplicated modules.
async function linker(specifier, referencingModule) {
if (specifier === 'foo') {
return new vm.SourceTextModule(`
// The "secret" variable refers to the global variable we added to
// "contextifiedObject" when creating the context.
export default secret;
`, { context: referencingModule.context });
// Using `contextifiedObject` instead of `referencingModule.context`
// here would work as well.
}
throw new Error(`Unable to resolve dependency: ${specifier}`);
}
await bar.link(linker);
// Step 3
//
// Evaluate the Module. The evaluate() method returns a promise which will
// resolve after the module has finished evaluating.
// Prints 42.
await bar.evaluate();
const vm = require('node:vm');
const contextifiedObject = vm.createContext({
secret: 42,
print: console.log,
});
(async () => {
// Step 1
//
// Create a Module by constructing a new `vm.SourceTextModule` object. This
// parses the provided source text, throwing a `SyntaxError` if anything goes
// wrong. By default, a Module is created in the top context. But here, we
// specify `contextifiedObject` as the context this Module belongs to.
//
// Here, we attempt to obtain the default export from the module "foo", and
// put it into local binding "secret".
const bar = new vm.SourceTextModule(`
import s from 'foo';
s;
print(s);
`, { context: contextifiedObject });
// Step 2
//
// "Link" the imported dependencies of this Module to it.
//
// The provided linking callback (the "linker") accepts two arguments: the
// parent module (`bar` in this case) and the string that is the specifier of
// the imported module. The callback is expected to return a Module that
// corresponds to the provided specifier, with certain requirements documented
// in `module.link()`.
//
// If linking has not started for the returned Module, the same linker
// callback will be called on the returned Module.
//
// Even top-level Modules without dependencies must be explicitly linked. The
// callback provided would never be called, however.
//
// The link() method returns a Promise that will be resolved when all the
// Promises returned by the linker resolve.
//
// Note: This is a contrived example in that the linker function creates a new
// "foo" module every time it is called. In a full-fledged module system, a
// cache would probably be used to avoid duplicated modules.
async function linker(specifier, referencingModule) {
if (specifier === 'foo') {
return new vm.SourceTextModule(`
// The "secret" variable refers to the global variable we added to
// "contextifiedObject" when creating the context.
export default secret;
`, { context: referencingModule.context });
// Using `contextifiedObject` instead of `referencingModule.context`
// here would work as well.
}
throw new Error(`Unable to resolve dependency: ${specifier}`);
}
await bar.link(linker);
// Step 3
//
// Evaluate the Module. The evaluate() method returns a promise which will
// resolve after the module has finished evaluating.
// Prints 42.
await bar.evaluate();
})();
module.dependencySpecifiers
#
このモジュールのすべての依存関係の指定子。返された配列は、変更を禁止するために凍結されます。
ECMAScript仕様の循環モジュールレコードの[[RequestedModules]]
フィールドに対応します。
module.error
#
module.status
が'errored'
の場合、このプロパティには、評価中にモジュールによってスローされた例外が含まれます。ステータスがそれ以外の場合、このプロパティにアクセスすると、例外がスローされます。
throw undefined;
とのあいまいさの可能性があるため、スローされた例外がない場合、値undefined
を使用することはできません。
ECMAScript仕様の循環モジュールレコードの[[EvaluationError]]
フィールドに対応します。
module.evaluate([options])
#
モジュールを評価します。
これは、モジュールがリンクされた後に呼び出す必要があります。それ以外の場合は拒否されます。また、モジュールが既に評価されている場合にも呼び出すことができます。この場合、最初の評価が成功した場合(module.status
が'evaluated'
の場合)は何もしないか、最初の評価で発生した例外を再スローします(module.status
が'errored'
の場合)。
このメソッドは、モジュールが評価中の場合(module.status
が'evaluating'
の場合)に呼び出すことはできません。
ECMAScript仕様の循環モジュールレコードのEvaluate() 具象メソッドフィールドに対応します。
module.identifier
#
コンストラクターで設定された、現在のモジュールの識別子。
module.link(linker)
#
linker
<Function>-
specifier
<string> 要求されたモジュールの指定子import foo from 'foo'; // ^^^^^ the module specifier
-
referencingModule
<vm.Module>link()
が呼び出されるModule
オブジェクト。 -
extra
<Object> -
戻り値: <vm.Module> | <Promise>
-
- 戻り値: <Promise>
モジュールの依存関係をリンクします。このメソッドは評価前に呼び出す必要があり、モジュールごとに1回だけ呼び出すことができます。
関数は、Module
オブジェクトまたは最終的にModule
オブジェクトに解決されるPromise
を返すことが期待されています。返されたModule
は、次の2つの不変条件を満たす必要があります。
- 親
Module
と同じコンテキストに属している必要があります。 - その
status
は'errored'
であってはなりません。
返されたModule
のstatus
が'unlinked'
の場合、このメソッドは、返されたModule
で同じ提供されたlinker
関数を使用して再帰的に呼び出されます。
link()
は、すべてのリンクインスタンスが有効なModule
に解決されたときに解決されるPromise
を返すか、リンカー関数が例外をスローするか、無効なModule
を返した場合に拒否されます。
リンカー関数は、ECMAScript仕様の、実装で定義されたHostResolveImportedModule抽象操作にほぼ対応していますが、いくつかの重要な違いがあります。
- リンカー関数は非同期にすることが許可されていますが、HostResolveImportedModuleは同期です。
モジュールリンキング中に使用される実際のHostResolveImportedModuleの実装は、リンキング中にリンクされたモジュールを返す実装です。その時点ですべてのモジュールが既に完全にリンクされているため、HostResolveImportedModuleの実装は仕様に従って完全に同期しています。
ECMAScript仕様の循環モジュールレコードのLink() 具象メソッドフィールドに対応します。
module.namespace
#
モジュールの名前空間オブジェクト。これは、リンク(module.link()
)が完了した後でのみ使用可能です。
ECMAScript仕様のGetModuleNamespace抽象操作に対応します。
module.status
#
モジュールの現在のステータス。次のいずれかになります
-
'unlinked'
:module.link()
はまだ呼び出されていません。 -
'linking'
:module.link()
は呼び出されましたが、リンカー関数によって返されたすべてのPromiseがまだ解決されていません。 -
'linked'
: モジュールは正常にリンクされ、そのすべての依存関係もリンクされていますが、module.evaluate()
はまだ呼び出されていません。 -
'evaluating'
: モジュール自身または親モジュールに対するmodule.evaluate()
を通して、モジュールが評価されています。 -
'evaluated'
: モジュールは正常に評価されました。 -
'errored'
: モジュールは評価されましたが、例外がスローされました。
'errored'
以外の場合、このステータス文字列は仕様の Cyclic Module Record の [[Status]]
フィールドに対応します。'errored'
は仕様では 'evaluated'
に対応しますが、[[EvaluationError]]
が undefined
ではない値に設定されています。
クラス: vm.SourceTextModule
#
この機能は、--experimental-vm-modules
コマンドフラグが有効になっている場合にのみ使用できます。
- 拡張: <vm.Module>
vm.SourceTextModule
クラスは、ECMAScript 仕様で定義されている Source Text Module Record を提供します。
new vm.SourceTextModule(code[, options])
#
code
<string> パースする JavaScript モジュールコードoptions
identifier
<string> スタックトレースで使用される文字列。デフォルト:'vm:module(i)'
。ここでi
はコンテキスト固有の昇順インデックスです。cachedData
<Buffer> | <TypedArray> | <DataView> 提供されたソースの V8 コードキャッシュデータを含む、オプションのBuffer
またはTypedArray
、またはDataView
を提供します。code
は、このcachedData
が作成されたモジュールと同じである必要があります。context
<Object> このModule
をコンパイルおよび評価する コンテキスト化されたオブジェクト。vm.createContext()
メソッドによって返されます。コンテキストが指定されていない場合、モジュールは現在の実行コンテキストで評価されます。lineOffset
<integer> このModule
によって生成されるスタックトレースに表示される行番号オフセットを指定します。デフォルト:0
。columnOffset
<integer> このModule
によって生成されるスタックトレースに表示される、1行目の列番号オフセットを指定します。デフォルト:0
。initializeImportMeta
<Function> このModule
の評価中にimport.meta
を初期化するために呼び出されます。meta
<import.meta>module
<vm.SourceTextModule>
importModuleDynamically
<Function>import()
が呼び出されたときに、このモジュールの評価中にモジュールをどのようにロードするかを指定するために使用されます。このオプションは、実験的なモジュール API の一部です。本番環境での使用は推奨されません。詳細については、コンパイル API における動的import()
のサポートを参照してください。
新しい SourceTextModule
インスタンスを作成します。
import.meta
オブジェクトに割り当てられたオブジェクト型のプロパティは、モジュールが指定された context
の外部の情報にアクセスすることを許可する可能性があります。特定のコンテキストでオブジェクトを作成するには、vm.runInContext()
を使用してください。
import vm from 'node:vm';
const contextifiedObject = vm.createContext({ secret: 42 });
const module = new vm.SourceTextModule(
'Object.getPrototypeOf(import.meta.prop).secret = secret;',
{
initializeImportMeta(meta) {
// Note: this object is created in the top context. As such,
// Object.getPrototypeOf(import.meta.prop) points to the
// Object.prototype in the top context rather than that in
// the contextified object.
meta.prop = {};
},
});
// Since module has no dependencies, the linker function will never be called.
await module.link(() => {});
await module.evaluate();
// Now, Object.prototype.secret will be equal to 42.
//
// To fix this problem, replace
// meta.prop = {};
// above with
// meta.prop = vm.runInContext('{}', contextifiedObject);
const vm = require('node:vm');
const contextifiedObject = vm.createContext({ secret: 42 });
(async () => {
const module = new vm.SourceTextModule(
'Object.getPrototypeOf(import.meta.prop).secret = secret;',
{
initializeImportMeta(meta) {
// Note: this object is created in the top context. As such,
// Object.getPrototypeOf(import.meta.prop) points to the
// Object.prototype in the top context rather than that in
// the contextified object.
meta.prop = {};
},
});
// Since module has no dependencies, the linker function will never be called.
await module.link(() => {});
await module.evaluate();
// Now, Object.prototype.secret will be equal to 42.
//
// To fix this problem, replace
// meta.prop = {};
// above with
// meta.prop = vm.runInContext('{}', contextifiedObject);
})();
sourceTextModule.createCachedData()
#
- 返り値: <Buffer>
SourceTextModule
コンストラクターの cachedData
オプションで使用できるコードキャッシュを作成します。Buffer
を返します。このメソッドは、モジュールが評価される前であれば何度でも呼び出すことができます。
SourceTextModule
のコードキャッシュには、JavaScript から観測できる状態は含まれていません。コードキャッシュは、スクリプトソースと一緒に保存し、新しい SourceTextModule
インスタンスを複数回構築するために使用しても安全です。
SourceTextModule
ソースの関数は遅延コンパイルとしてマークでき、SourceTextModule
の構築時にはコンパイルされません。これらの関数は、最初に呼び出されたときにコンパイルされます。コードキャッシュは、V8 が現在 SourceTextModule
について認識しているメタデータをシリアル化し、将来のコンパイルを高速化するために使用できます。
// Create an initial module
const module = new vm.SourceTextModule('const a = 1;');
// Create cached data from this module
const cachedData = module.createCachedData();
// Create a new module using the cached data. The code must be the same.
const module2 = new vm.SourceTextModule('const a = 1;', { cachedData });
クラス: vm.SyntheticModule
#
この機能は、--experimental-vm-modules
コマンドフラグが有効になっている場合にのみ使用できます。
- 拡張: <vm.Module>
vm.SyntheticModule
クラスは、WebIDL 仕様で定義されている Synthetic Module Record を提供します。合成モジュールの目的は、非 JavaScript ソースを ECMAScript モジュールグラフに公開するための汎用的なインターフェースを提供することです。
const vm = require('node:vm');
const source = '{ "a": 1 }';
const module = new vm.SyntheticModule(['default'], function() {
const obj = JSON.parse(source);
this.setExport('default', obj);
});
// Use `module` in linking...
new vm.SyntheticModule(exportNames, evaluateCallback[, options])
#
exportNames
<string[]> モジュールからエクスポートされる名前の配列。evaluateCallback
<Function> モジュールが評価されるときに呼び出されます。options
新しい SyntheticModule
インスタンスを作成します。
このインスタンスのエクスポートに割り当てられたオブジェクトは、モジュールのインポーターが指定された context
の外部の情報にアクセスすることを許可する可能性があります。特定のコンテキストでオブジェクトを作成するには、vm.runInContext()
を使用してください。
syntheticModule.setExport(name, value)
#
このメソッドは、モジュールがリンクされた後、エクスポートの値を設定するために使用されます。モジュールがリンクされる前に呼び出された場合、ERR_VM_MODULE_STATUS
エラーがスローされます。
import vm from 'node:vm';
const m = new vm.SyntheticModule(['x'], () => {
m.setExport('x', 1);
});
await m.link(() => {});
await m.evaluate();
assert.strictEqual(m.namespace.x, 1);
const vm = require('node:vm');
(async () => {
const m = new vm.SyntheticModule(['x'], () => {
m.setExport('x', 1);
});
await m.link(() => {});
await m.evaluate();
assert.strictEqual(m.namespace.x, 1);
})();
vm.compileFunction(code[, params[, options]])
#
code
<string> コンパイルする関数の本体。params
<string[]> 関数のすべてのパラメータを含む文字列の配列。options
<Object>filename
<string> このスクリプトによって生成されるスタックトレースで使用されるファイル名を指定します。デフォルト:''
。lineOffset
<number> このスクリプトによって生成されたスタックトレースに表示される行番号オフセットを指定します。既定値:0
。columnOffset
<number> このスクリプトによって生成されたスタックトレースに表示される 1 行目の列番号オフセットを指定します。既定値:0
。cachedData
<Buffer> | <TypedArray> | <DataView> 提供されたソースの V8 コードキャッシュデータを含む、オプションのBuffer
またはTypedArray
、またはDataView
を提供します。これは、同じcode
とparams
を使用した以前のvm.compileFunction()
の呼び出しによって生成されている必要があります。produceCachedData
<boolean> 新しいキャッシュデータを生成するかどうかを指定します。デフォルト:false
。parsingContext
<Object> 問題の関数をコンパイルする必要がある コンテキスト化されたオブジェクト。contextExtensions
<Object[]> コンパイル中に適用されるコンテキスト拡張機能(現在のスコープをラップするオブジェクト)のコレクションを含む配列。デフォルト:[]
。
importModuleDynamically
<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER>import()
が呼び出されたときに、この関数の評価中にモジュールをどのようにロードするかを指定するために使用されます。このオプションは、実験的なモジュール API の一部です。本番環境での使用は推奨されません。詳細については、コンパイル API における動的import()
のサポートを参照してください。- 戻り値: <Function>
指定されたコードを(コンテキストが指定されていない場合は現在のコンテキストを使用して)指定されたコンテキストにコンパイルし、与えられた params
を持つ関数でラップして返します。
vm.constants
#
VM 操作で一般的に使用される定数を含むオブジェクトを返します。
vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER
#
vm.Script
および vm.compileFunction()
の importModuleDynamically
オプションとして使用できる定数。Node.js がメインコンテキストのデフォルトの ESM ローダーを使用して、要求されたモジュールをロードするようにします。
詳細については、コンパイル API における動的 import()
のサポートを参照してください。
vm.createContext([contextObject[, options]])
#
contextObject
<Object>options
<Object>name
<string> 新しく作成されたコンテキストの人間が読める名前。デフォルト:'VM Context i'
。ここでi
は作成されたコンテキストの昇順の数値インデックスです。origin
<string> オリジン は、表示目的で新しく作成されたコンテキストに対応します。オリジンは URL のようにフォーマットする必要がありますが、スキーム、ホスト、およびポート(必要な場合)のみを含める必要があります。これは、url.origin
プロパティの値のようなものです。URL
オブジェクト。特に、この文字列は末尾のスラッシュを省略する必要があります。これはパスを示すためです。デフォルト:''
。codeGeneration
<Object>microtaskMode
<string>afterEvaluate
に設定すると、マイクロタスク (Promise
およびasync function
を通じてスケジュールされたタスク) は、script.runInContext()
を通してスクリプトが実行された直後に実行されます。この場合、マイクロタスクはtimeout
およびbreakOnSigint
のスコープに含まれます。importModuleDynamically
<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> このコンテキストでリファラースクリプトまたはモジュールなしでimport()
が呼び出されたときにモジュールをロードする方法を指定するために使用します。このオプションは、実験的なモジュール API の一部です。本番環境での使用はお勧めしません。詳細については、コンパイル API における動的import()
のサポート を参照してください。
- 戻り値:<Object> コンテキスト化されたオブジェクト。
contextObject
が指定された場合、vm.createContext()
メソッドは そのオブジェクトを準備 して、vm.runInContext()
または script.runInContext()
の呼び出しで使用できるようにします。そのようなスクリプト内では、contextObject
はグローバルオブジェクトとなり、既存のすべてのプロパティを保持しますが、標準の グローバルオブジェクト が持つすべての組み込みオブジェクトと関数も持ちます。vm モジュールによって実行されるスクリプトの外部では、グローバル変数は変更されません。
const vm = require('node:vm');
global.globalVar = 3;
const context = { globalVar: 1 };
vm.createContext(context);
vm.runInContext('globalVar *= 2;', context);
console.log(context);
// Prints: { globalVar: 2 }
console.log(global.globalVar);
// Prints: 3
contextObject
が省略された(または undefined
として明示的に渡された)場合、新しく空の コンテキスト化された オブジェクトが返されます。
vm.createContext()
メソッドは主に、複数のスクリプトを実行するために使用できる単一のコンテキストを作成するのに役立ちます。たとえば、Web ブラウザをエミュレートする場合、このメソッドを使用して、ウィンドウのグローバルオブジェクトを表す単一のコンテキストを作成し、そのコンテキスト内で <script>
タグをすべてまとめて実行できます。
コンテキストに提供された name
および origin
は、インスペクター API を通して表示されます。
vm.isContext(object)
#
指定された object
オブジェクトが vm.createContext()
を使用してコンテキスト化されている場合は true
を返します。
vm.measureMemory([options])
#
現在の V8 アイソレート、またはメインコンテキストに既知のすべてのコンテキストによって使用される V8 に既知のメモリを測定します。
options
<Object> オプション。mode
<string>'summary'
または'detailed'
のいずれかです。summary モードでは、メインコンテキストで測定されたメモリのみが返されます。detailed モードでは、現在の V8 アイソレートに既知のすべてのコンテキストで測定されたメモリが返されます。デフォルト:'summary'
execution
<string>'default'
または'eager'
のいずれかです。デフォルトの実行では、次のスケジュールされたガベージコレクションが開始されるまで(または、次の GC の前にプログラムが終了する場合は決して) Promise は解決されません。eager 実行では、メモリを測定するために GC がすぐに開始されます。デフォルト:'default'
- 戻り値:<Promise> メモリが正常に測定された場合、Promise はメモリ使用量に関する情報を含むオブジェクトで解決されます。それ以外の場合は、
ERR_CONTEXT_NOT_INITIALIZED
エラーで拒否されます。
返される Promise が解決される可能性のあるオブジェクトの形式は、V8 エンジンに固有であり、V8 のバージョンごとに変更される可能性があります。
返される結果は、v8.getHeapSpaceStatistics()
によって返される統計とは異なります。vm.measureMemory()
は、現在の V8 エンジンのインスタンス内の各 V8 特定のコンテキストによって到達可能なメモリを測定しますが、v8.getHeapSpaceStatistics()
の結果は、現在の V8 インスタンス内の各ヒープスペースによって占有されるメモリを測定します。
const vm = require('node:vm');
// Measure the memory used by the main context.
vm.measureMemory({ mode: 'summary' })
// This is the same as vm.measureMemory()
.then((result) => {
// The current format is:
// {
// total: {
// jsMemoryEstimate: 2418479, jsMemoryRange: [ 2418479, 2745799 ]
// }
// }
console.log(result);
});
const context = vm.createContext({ a: 1 });
vm.measureMemory({ mode: 'detailed', execution: 'eager' })
.then((result) => {
// Reference the context here so that it won't be GC'ed
// until the measurement is complete.
console.log(context.a);
// {
// total: {
// jsMemoryEstimate: 2574732,
// jsMemoryRange: [ 2574732, 2904372 ]
// },
// current: {
// jsMemoryEstimate: 2438996,
// jsMemoryRange: [ 2438996, 2768636 ]
// },
// other: [
// {
// jsMemoryEstimate: 135736,
// jsMemoryRange: [ 135736, 465376 ]
// }
// ]
// }
console.log(result);
});
vm.runInContext(code, contextifiedObject[, options])
#
code
<string> コンパイルおよび実行する JavaScript コード。contextifiedObject
<Object>code
がコンパイルおよび実行されるときにglobal
として使用される、コンテキスト化された オブジェクト。options
<Object> | <string>filename
<string> このスクリプトによって生成されたスタックトレースで使用されるファイル名を指定します。既定値:'evalmachine.<anonymous>'
。lineOffset
<number> このスクリプトによって生成されたスタックトレースに表示される行番号オフセットを指定します。既定値:0
。columnOffset
<number> このスクリプトによって生成されたスタックトレースに表示される 1 行目の列番号オフセットを指定します。既定値:0
。displayErrors
<boolean>true
の場合、code
のコンパイル中にError
が発生した場合、エラーの原因となったコード行がスタックトレースに付加されます。既定値:true
。timeout
<integer> 実行を終了する前にcode
を実行するミリ秒数を指定します。実行が終了すると、Error
がスローされます。この値は、厳密な正の整数である必要があります。breakOnSigint
<boolean>true
の場合、SIGINT
(Ctrl+C) を受信すると、実行が終了し、Error
がスローされます。process.on('SIGINT')
を介してアタッチされたイベントの既存のハンドラーは、スクリプトの実行中は無効になりますが、その後も引き続き機能します。既定値:false
。cachedData
<Buffer> | <TypedArray> | <DataView> 提供されたソースの V8 コードキャッシュデータを含むオプションのBuffer
、TypedArray
、またはDataView
を提供します。importModuleDynamically
<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER>import()
が呼び出されたときに、このスクリプトの評価中にモジュールをロードする方法を指定するために使用します。このオプションは、実験的なモジュール API の一部です。本番環境での使用はお勧めしません。詳細については、コンパイル API における動的import()
のサポート を参照してください。
vm.runInContext()
メソッドは、code
をコンパイルし、contextifiedObject
のコンテキスト内で実行してから、結果を返します。実行中のコードはローカルスコープにアクセスできません。contextifiedObject
オブジェクトは、vm.createContext()
メソッドを使用して事前にコンテキスト化されている必要があります。
options
が文字列の場合、ファイル名を指定します。
次の例は、単一の コンテキスト化された オブジェクトを使用して、さまざまなスクリプトをコンパイルおよび実行します。
const vm = require('node:vm');
const contextObject = { globalVar: 1 };
vm.createContext(contextObject);
for (let i = 0; i < 10; ++i) {
vm.runInContext('globalVar *= 2;', contextObject);
}
console.log(contextObject);
// Prints: { globalVar: 1024 }
vm.runInNewContext(code[, contextObject[, options]])
#
code
<string> コンパイルおよび実行する JavaScript コード。contextObject
<Object> コンテキスト化されるオブジェクト。undefined
の場合、新しいオブジェクトが作成されます。options
<Object> | <string>filename
<string> このスクリプトによって生成されたスタックトレースで使用されるファイル名を指定します。既定値:'evalmachine.<anonymous>'
。lineOffset
<number> このスクリプトによって生成されたスタックトレースに表示される行番号オフセットを指定します。既定値:0
。columnOffset
<number> このスクリプトによって生成されたスタックトレースに表示される 1 行目の列番号オフセットを指定します。既定値:0
。displayErrors
<boolean>true
の場合、code
のコンパイル中にError
が発生した場合、エラーの原因となったコード行がスタックトレースに付加されます。既定値:true
。timeout
<integer> 実行を終了する前にcode
を実行するミリ秒数を指定します。実行が終了すると、Error
がスローされます。この値は、厳密な正の整数である必要があります。breakOnSigint
<boolean>true
の場合、SIGINT
(Ctrl+C) を受信すると、実行が終了し、Error
がスローされます。process.on('SIGINT')
を介してアタッチされたイベントの既存のハンドラーは、スクリプトの実行中は無効になりますが、その後も引き続き機能します。既定値:false
。contextName
<string> 新しく作成されたコンテキストの人間が読める名前。デフォルト:'VM Context i'
。ここで、i
は作成されたコンテキストの昇順の数値インデックスです。contextOrigin
<string> 表示目的で、新しく作成されたコンテキストに対応するオリジン。オリジンはURLのようにフォーマットする必要がありますが、スキーム、ホスト、ポート(必要な場合)のみを含み、url.origin
プロパティの値のように、URL
オブジェクト。最も重要なのは、この文字列は、パスを示す末尾のスラッシュを省略する必要があることです。デフォルト:''
。contextCodeGeneration
<Object>cachedData
<Buffer> | <TypedArray> | <DataView> 提供されたソースの V8 コードキャッシュデータを含むオプションのBuffer
、TypedArray
、またはDataView
を提供します。importModuleDynamically
<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER>import()
が呼び出されたときに、このスクリプトの評価中にモジュールをロードする方法を指定するために使用します。このオプションは、実験的なモジュール API の一部です。本番環境での使用はお勧めしません。詳細については、コンパイル API における動的import()
のサポート を参照してください。microtaskMode
<string>afterEvaluate
に設定すると、マイクロタスク(Promise
とasync function
によってスケジュールされたタスク)は、スクリプトの実行直後に実行されます。この場合、それらはtimeout
およびbreakOnSigint
のスコープに含まれます。
- 返り値: <any> スクリプトで実行された最後のステートメントの結果。
vm.runInNewContext()
は、最初に指定された contextObject
をコンテキスト化(または、undefined
として渡された場合は新しい contextObject
を作成)し、code
をコンパイルし、作成されたコンテキスト内で実行してから、結果を返します。実行中のコードはローカルスコープにアクセスできません。
options
が文字列の場合、ファイル名を指定します。
次の例は、グローバル変数をインクリメントし、新しいグローバル変数を設定するコードをコンパイルおよび実行します。これらのグローバル変数は、contextObject
に含まれています。
const vm = require('node:vm');
const contextObject = {
animal: 'cat',
count: 2,
};
vm.runInNewContext('count += 1; name = "kitty"', contextObject);
console.log(contextObject);
// Prints: { animal: 'cat', count: 3, name: 'kitty' }
vm.runInThisContext(code[, options])
#
code
<string> コンパイルおよび実行する JavaScript コード。options
<Object> | <string>filename
<string> このスクリプトによって生成されたスタックトレースで使用されるファイル名を指定します。既定値:'evalmachine.<anonymous>'
。lineOffset
<number> このスクリプトによって生成されたスタックトレースに表示される行番号オフセットを指定します。既定値:0
。columnOffset
<number> このスクリプトによって生成されたスタックトレースに表示される 1 行目の列番号オフセットを指定します。既定値:0
。displayErrors
<boolean>true
の場合、code
のコンパイル中にError
が発生した場合、エラーの原因となったコード行がスタックトレースに付加されます。既定値:true
。timeout
<integer> 実行を終了する前にcode
を実行するミリ秒数を指定します。実行が終了すると、Error
がスローされます。この値は、厳密な正の整数である必要があります。breakOnSigint
<boolean>true
の場合、SIGINT
(Ctrl+C) を受信すると、実行が終了し、Error
がスローされます。process.on('SIGINT')
を介してアタッチされたイベントの既存のハンドラーは、スクリプトの実行中は無効になりますが、その後も引き続き機能します。既定値:false
。cachedData
<Buffer> | <TypedArray> | <DataView> 提供されたソースの V8 コードキャッシュデータを含むオプションのBuffer
、TypedArray
、またはDataView
を提供します。importModuleDynamically
<Function> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER>import()
が呼び出されたときに、このスクリプトの評価中にモジュールをロードする方法を指定するために使用します。このオプションは、実験的なモジュール API の一部です。本番環境での使用はお勧めしません。詳細については、コンパイル API における動的import()
のサポート を参照してください。
- 返り値: <any> スクリプトで実行された最後のステートメントの結果。
vm.runInThisContext()
は code
をコンパイルし、現在の global
のコンテキスト内で実行して結果を返します。実行中のコードはローカルスコープにアクセスできませんが、現在の global
オブジェクトにはアクセスできます。
options
が文字列の場合、ファイル名を指定します。
次の例は、同じコードを実行するために vm.runInThisContext()
と JavaScript の eval()
関数の両方を使用する方法を示しています。
const vm = require('node:vm');
let localVar = 'initial value';
const vmResult = vm.runInThisContext('localVar = "vm";');
console.log(`vmResult: '${vmResult}', localVar: '${localVar}'`);
// Prints: vmResult: 'vm', localVar: 'initial value'
const evalResult = eval('localVar = "eval";');
console.log(`evalResult: '${evalResult}', localVar: '${localVar}'`);
// Prints: evalResult: 'eval', localVar: 'eval'
vm.runInThisContext()
はローカルスコープにアクセスできないため、localVar
は変更されません。対照的に、eval()
はローカルスコープにアクセスできるため、値 localVar
が変更されます。このように、vm.runInThisContext()
は、間接的な eval()
呼び出し (例: (0,eval)('code')
) によく似ています。
例:VM 内で HTTP サーバーを実行する#
script.runInThisContext()
または vm.runInThisContext()
のいずれかを使用する場合、コードは現在の V8 グローバルコンテキスト内で実行されます。この VM コンテキストに渡されるコードには、独自の隔離されたスコープがあります。
node:http
モジュールを使用して簡単な Web サーバーを実行するには、コンテキストに渡されるコード自体が require('node:http')
を呼び出すか、または node:http
モジュールへの参照が渡される必要があります。例えば
'use strict';
const vm = require('node:vm');
const code = `
((require) => {
const http = require('node:http');
http.createServer((request, response) => {
response.writeHead(200, { 'Content-Type': 'text/plain' });
response.end('Hello World\\n');
}).listen(8124);
console.log('Server running at http://127.0.0.1:8124/');
})`;
vm.runInThisContext(code)(require);
上記の例の require()
は、渡されたコンテキストと状態を共有します。これにより、信頼できないコードが実行される場合に、たとえばコンテキスト内のオブジェクトを予期しない方法で変更するなど、リスクが生じる可能性があります。
オブジェクトを「コンテキスト化」するとはどういう意味ですか?#
Node.js 内で実行されるすべての JavaScript は、「コンテキスト」のスコープ内で実行されます。V8 エンベッダーのガイド によると、
V8 では、コンテキストは、個別の関連性のない JavaScript アプリケーションが V8 の単一インスタンスで実行できるようにする実行環境です。JavaScript コードを実行するコンテキストを明示的に指定する必要があります。
メソッド vm.createContext()
が呼び出されると、contextObject
引数 (または contextObject
が undefined
の場合は新しく作成されたオブジェクト) は、内部的に V8 コンテキストの新しいインスタンスに関連付けられます。この V8 コンテキストは、node:vm
モジュールのメソッドを使用して実行される code
に、操作可能な隔離されたグローバル環境を提供します。V8 コンテキストを作成し、それを contextObject
に関連付けるプロセスが、このドキュメントでオブジェクトの「コンテキスト化」と呼んでいるものです。
非同期タスクと Promise とのタイムアウトの相互作用#
Promise
とasync function
は、JavaScriptエンジンによって非同期に実行されるタスクをスケジュールできます。デフォルトでは、これらのタスクは現在のスタック上のすべてのJavaScript関数の実行が完了した後に実行されます。これにより、timeout
とbreakOnSigint
オプションの機能を回避できます。
例えば、vm.runInNewContext()
によって実行され、タイムアウトが5ミリ秒に設定された次のコードは、Promiseが解決された後に実行される無限ループをスケジュールします。スケジュールされたループはタイムアウトによって中断されることはありません。
const vm = require('node:vm');
function loop() {
console.log('entering loop');
while (1) console.log(Date.now());
}
vm.runInNewContext(
'Promise.resolve().then(() => loop());',
{ loop, console },
{ timeout: 5 },
);
// This is printed *before* 'entering loop' (!)
console.log('done executing');
これは、Context
を作成するコードにmicrotaskMode: 'afterEvaluate'
を渡すことで対処できます。
const vm = require('node:vm');
function loop() {
while (1) console.log(Date.now());
}
vm.runInNewContext(
'Promise.resolve().then(() => loop());',
{ loop, console },
{ timeout: 5, microtaskMode: 'afterEvaluate' },
);
この場合、promise.then()
を通じてスケジュールされたマイクロタスクは、vm.runInNewContext()
から戻る前に実行され、timeout
機能によって中断されます。これはvm.Context
で実行されているコードにのみ適用されるため、例えばvm.runInThisContext()
はこのオプションを受け取りません。
Promiseのコールバックは、それらが作成されたコンテキストのマイクロタスクキューに入力されます。例えば、上記の例で() => loop()
を単にloop
に置き換えた場合、loop
はグローバルマイクロタスクキューにプッシュされます。これは外側の(メインの)コンテキストからの関数であるため、タイムアウトを回避することもできます。
process.nextTick()
、queueMicrotask()
、setTimeout()
、setImmediate()
などの非同期スケジューリング関数がvm.Context
内で利用可能になっている場合、それらに渡された関数はグローバルキューに追加され、すべてのコンテキストで共有されます。したがって、これらの関数に渡されたコールバックもタイムアウトによって制御できません。
コンパイルAPIにおける動的なimport()
のサポート#
次のAPIは、vmモジュールによってコンパイルされたコードで動的なimport()
を有効にするためのimportModuleDynamically
オプションをサポートしています。
new vm.Script
vm.compileFunction()
new vm.SourceTextModule
vm.runInThisContext()
vm.runInContext()
vm.runInNewContext()
vm.createContext()
このオプションはまだ実験的なモジュールAPIの一部です。本番環境での使用はお勧めしません。
importModuleDynamically
オプションが指定されていないか、未定義の場合#
このオプションが指定されていない場合、またはundefined
である場合、import()
を含むコードはvm APIによってコンパイルできます。ただし、コンパイルされたコードが実行され、実際にimport()
を呼び出すと、結果はERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING
でリジェクトされます。
importModuleDynamically
がvm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER
の場合#
このオプションは、現在vm.SourceTextModule
ではサポートされていません。
このオプションを使用すると、コンパイルされたコードでimport()
が開始された場合、Node.jsはメインコンテキストのデフォルトのESMローダーを使用して、要求されたモジュールをロードし、実行中のコードに返します。
これにより、fs
やhttp
などのNode.js組み込みモジュールへのアクセスが、コンパイルされているコードに提供されます。コードが異なるコンテキストで実行される場合、メインコンテキストからロードされたモジュールによって作成されたオブジェクトは、新しいコンテキストの組み込みクラスのinstanceof
ではなく、依然としてメインコンテキストのものであることに注意してください。
const { Script, constants } = require('node:vm');
const script = new Script(
'import("node:fs").then(({readFile}) => readFile instanceof Function)',
{ importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER });
// false: URL loaded from the main context is not an instance of the Function
// class in the new context.
script.runInNewContext().then(console.log);
import { Script, constants } from 'node:vm';
const script = new Script(
'import("node:fs").then(({readFile}) => readFile instanceof Function)',
{ importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER });
// false: URL loaded from the main context is not an instance of the Function
// class in the new context.
script.runInNewContext().then(console.log);
このオプションを使用すると、スクリプトまたは関数がユーザーモジュールをロードすることもできます。
import { Script, constants } from 'node:vm';
import { resolve } from 'node:path';
import { writeFileSync } from 'node:fs';
// Write test.js and test.txt to the directory where the current script
// being run is located.
writeFileSync(resolve(import.meta.dirname, 'test.mjs'),
'export const filename = "./test.json";');
writeFileSync(resolve(import.meta.dirname, 'test.json'),
'{"hello": "world"}');
// Compile a script that loads test.mjs and then test.json
// as if the script is placed in the same directory.
const script = new Script(
`(async function() {
const { filename } = await import('./test.mjs');
return import(filename, { with: { type: 'json' } })
})();`,
{
filename: resolve(import.meta.dirname, 'test-with-default.js'),
importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
});
// { default: { hello: 'world' } }
script.runInThisContext().then(console.log);
const { Script, constants } = require('node:vm');
const { resolve } = require('node:path');
const { writeFileSync } = require('node:fs');
// Write test.js and test.txt to the directory where the current script
// being run is located.
writeFileSync(resolve(__dirname, 'test.mjs'),
'export const filename = "./test.json";');
writeFileSync(resolve(__dirname, 'test.json'),
'{"hello": "world"}');
// Compile a script that loads test.mjs and then test.json
// as if the script is placed in the same directory.
const script = new Script(
`(async function() {
const { filename } = await import('./test.mjs');
return import(filename, { with: { type: 'json' } })
})();`,
{
filename: resolve(__dirname, 'test-with-default.js'),
importModuleDynamically: constants.USE_MAIN_CONTEXT_DEFAULT_LOADER,
});
// { default: { hello: 'world' } }
script.runInThisContext().then(console.log);
メインコンテキストのデフォルトローダーを使用してユーザーモジュールをロードする場合、いくつかの注意点があります。
- 解決されるモジュールは、
vm.Script
またはvm.compileFunction()
に渡されたfilename
オプションを基準にします。解決は、絶対パスまたはURL文字列のいずれかであるfilename
で機能します。filename
が絶対パスまたはURLのいずれでもない文字列である場合、または未定義である場合、解決はプロセスの現在の作業ディレクトリを基準にします。vm.createContext()
の場合、このオプションはリファラースクリプトまたはモジュールがない場合にのみ使用されるため、解決は常に現在の作業ディレクトリを基準にします。 - 特定のパスに解決される任意の
filename
について、プロセスがそのパスから特定のモジュールをロードすると、結果がキャッシュされる可能性があり、同じパスからの同じモジュールの後続のロードは同じものを返します。filename
がURL文字列の場合、検索パラメーターが異なる場合はキャッシュがヒットしません。URL文字列ではないfilename
の場合、現時点ではキャッシュの動作を回避する方法はありません。
importModuleDynamically
が関数の場合#
importModuleDynamically
が関数の場合、コンパイルされたコードでimport()
が呼び出されたときに、要求されたモジュールをコンパイルおよび評価する方法をカスタマイズするために呼び出されます。現在、このオプションを機能させるには、Node.jsインスタンスを--experimental-vm-modules
フラグを指定して起動する必要があります。フラグが設定されていない場合、このコールバックは無視されます。評価されたコードが実際にimport()
を呼び出した場合、結果はERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG
でリジェクトされます。
コールバックimportModuleDynamically(specifier, referrer, importAttributes)
には、次のシグネチャがあります。
specifier
<string>import()
に渡される指定子referrer
<vm.Script> | <Function> | <vm.SourceTextModule> | <Object> リファラーは、new vm.Script
、vm.runInThisContext
、vm.runInContext
、vm.runInNewContext
の場合はコンパイルされたvm.Script
です。vm.compileFunction
の場合はコンパイルされたFunction
、new vm.SourceTextModule
の場合はコンパイルされたvm.SourceTextModule
、vm.createContext()
の場合はコンテキストObject
です。importAttributes
<Object>optionsExpression
のオプションパラメーターに渡される"with"
値、または値が提供されていない場合は空のオブジェクト。- 戻り値: <モジュール名前空間オブジェクト> | <vm.Module> エラー追跡を利用し、
then
関数エクスポートを含む名前空間の問題を回避するためには、vm.Module
を返すことをお勧めします。
// This script must be run with --experimental-vm-modules.
import { Script, SyntheticModule } from 'node:vm';
const script = new Script('import("foo.json", { with: { type: "json" } })', {
async importModuleDynamically(specifier, referrer, importAttributes) {
console.log(specifier); // 'foo.json'
console.log(referrer); // The compiled script
console.log(importAttributes); // { type: 'json' }
const m = new SyntheticModule(['bar'], () => { });
await m.link(() => { });
m.setExport('bar', { hello: 'world' });
return m;
},
});
const result = await script.runInThisContext();
console.log(result); // { bar: { hello: 'world' } }
// This script must be run with --experimental-vm-modules.
const { Script, SyntheticModule } = require('node:vm');
(async function main() {
const script = new Script('import("foo.json", { with: { type: "json" } })', {
async importModuleDynamically(specifier, referrer, importAttributes) {
console.log(specifier); // 'foo.json'
console.log(referrer); // The compiled script
console.log(importAttributes); // { type: 'json' }
const m = new SyntheticModule(['bar'], () => { });
await m.link(() => { });
m.setExport('bar', { hello: 'world' });
return m;
},
});
const result = await script.runInThisContext();
console.log(result); // { bar: { hello: 'world' } }
})();