VM (выполнение JavaScript)
Стабильность: 2 — Стабильный
Модуль node:vm позволяет компилировать и выполнять код в контекстах виртуальной машины V8.
Модуль node:vm не является средством обеспечения безопасности. Не используйте его для выполнения недоверенного кода.
Код JavaScript можно скомпилировать и выполнить сразу либо скомпилировать, сохранить и выполнить позже.
Один из распространённых вариантов использования — выполнение кода в другом контексте V8. Это означает, что у вызываемого кода другой глобальный объект, чем у вызывающего кода.
Контекст можно задать, контекстуализировав объект. Вызываемый код рассматривает любое свойство контекста как глобальную переменную. Любые изменения глобальных переменных, вызванные вызываемым кодом, отражаются в объекте контекста.
Модули JavaScript
import { createContext, runInContext } from 'node:vm';
const x = 1;
const context = { x: 2 };
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.
runInContext(code, context);
console.log(context.x); // 42
console.log(context.y); // 17
console.log(x); // 1; y is not defined
CommonJS
const { createContext, runInContext } = require('node:vm');
const x = 1;
const context = { x: 2 };
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.
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>Задаёт смещение номера столбца первой строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию:0. -
cachedData<Buffer>|<TypedArray>|<DataView>Предоставляет необязательныйBufferилиTypedArrayлибоDataViewс данными кэша кода V8 для указанного исходного кода. Если он указан, значениеcachedDataRejectedбудет установлено вtrueилиfalseв зависимости от того, приняла ли V8 эти данные. -
produceCachedData<boolean>Если заданоtrueи параметрcachedDataотсутствует, V8 попытается создать данные кэша кода дляcode. В случае успеха будет созданBufferс данными кэша кода V8 и сохранён в свойствеcachedDataвозвращённого экземпляраvm.Script. ЗначениеcachedDataProducedбудет установлено вtrueилиfalseв зависимости от того, удалось ли создать данные кэша кода. Этот параметр устарел и заменён наscript.createCachedData(). По умолчанию:false. -
importModuleDynamically<Function>|<vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER>Используется для указания способа загрузки модулей при вычислении этого скрипта, когда вызываетсяimport(). Этот параметр входит в экспериментальный API модулей. Не рекомендуется использовать его в производственной среде. Подробные сведения см. в разделе Поддержка динамическогоimport()в API компиляции.
-
Если options — строка, она задаёт имя файла.
Создание нового объекта vm.Script компилирует code, но не выполняет его. Скомпилированный vm.Script можно выполнить позже несколько раз. code не привязан к какому-либо глобальному объекту; вместо этого он привязывается перед каждым запуском и только на время этого запуска.
script.cachedDataRejected
- Тип:
<boolean>|<undefined>
Если при создании vm.Script указан параметр cachedData, это значение будет установлено в true или false в зависимости от того, приняла ли V8 эти данные. В противном случае значение равно undefined.
script.createCachedData()
- Возвращает:
<Buffer>
Создаёт кэш кода, который можно использовать с параметром cachedData конструктора Script. Возвращает 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()`.
jscopy
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>результат последнего выполненного в скрипте оператора.
Выполняет скомпилированный код, содержащийся в объекте vm.Script, в заданном contextifiedObject и возвращает результат. Выполняемый код не имеет доступа к локальной области видимости.
В следующем примере компилируется код, который увеличивает глобальную переменную, задаёт значение другой глобальной переменной, а затем выполняется несколько раз. Глобальные переменные содержатся в объекте context.
Модули JavaScript
import { createContext, Script } from 'node:vm';
const context = {
animal: 'cat',
count: 2,
};
const script = new Script('count += 1; name = "kitty";');
createContext(context);
for (let i = 0; i < 10; ++i) {
script.runInContext(context);
}
console.log(context);
// Prints: { animal: 'cat', count: 12, name: 'kitty' }
CommonJS
const { createContext, Script } = require('node:vm');
const context = {
animal: 'cat',
count: 2,
};
const script = new Script('count += 1; name = "kitty";');
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>|<vm.constants.DONT_CONTEXTIFY>|<undefined>Либоvm.constants.DONT_CONTEXTIFY, либо объект, который будет контекстуализирован. Если задано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>-
strings<boolean>Если задать значение false, любые вызовыevalили конструкторов функций (Function,GeneratorFunctionи т. д.) приведут к выбросуEvalError. По умолчанию:true. -
wasm<boolean>Если задать значение false, любая попытка скомпилировать модуль WebAssembly приведёт к выбросуWebAssembly.CompileError. По умолчанию:true.
-
-
microtaskMode<string>Если задано значениеafterEvaluate, микрозадачи (задачи, запланированные черезPromiseиasync function) будут выполняться сразу после завершения скрипта. В этом случае они включаются в области видимостиtimeoutиbreakOnSigint.
-
- Возвращает:
<any>результат последнего выполненного в скрипте оператора.
Этот метод является сокращённой формой вызова script.runInContext(vm.createContext(options), options). Он выполняет сразу несколько действий:
- Создаёт новый контекст.
- Если
contextObject— объект, он контекстуализируется в новом контексте. ЕслиcontextObjectне определён, создаётся новый объект и контекстуализируется. ЕслиcontextObject—vm.constants.DONT_CONTEXTIFY, ничего не контекстуализируется. - Выполняет скомпилированный код, содержащийся в объекте
vm.Script, в созданном контексте. Код не имеет доступа к области видимости, в которой вызван этот метод. - Возвращает результат.
В следующем примере компилируется код, который задаёт глобальную переменную, а затем несколько раз выполняет этот код в разных контекстах. Глобальные переменные задаются в каждом отдельном объекте context и содержатся в нём.
Модули JavaScript
import { constants, Script } from 'node:vm';
const script = new Script('globalVar = "set"');
const contexts = [{}, {}, {}];
contexts.forEach((context) => {
script.runInNewContext(context);
});
console.log(contexts);
// Prints: [{ globalVar: 'set' }, { globalVar: 'set' }, { globalVar: 'set' }]
// This would throw if the context is created from a contextified object.
// constants.DONT_CONTEXTIFY allows creating contexts with ordinary
// global objects that can be frozen.
const freezeScript = new Script('Object.freeze(globalThis); globalThis;');
const frozenContext = freezeScript.runInNewContext(constants.DONT_CONTEXTIFY);
CommonJS
const { constants, Script } = require('node:vm');
const script = new Script('globalVar = "set"');
const contexts = [{}, {}, {}];
contexts.forEach((context) => {
script.runInNewContext(context);
});
console.log(contexts);
// Prints: [{ globalVar: 'set' }, { globalVar: 'set' }, { globalVar: 'set' }]
// This would throw if the context is created from a contextified object.
// constants.DONT_CONTEXTIFY allows creating contexts with ordinary
// global objects that can be frozen.
const freezeScript = new Script('Object.freeze(globalThis); globalThis;');
const frozenContext = freezeScript.runInNewContext(constants.DONT_CONTEXTIFY);
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>результат последнего выполненного в скрипте оператора.
Выполняет скомпилированный код, содержащийся в vm.Script, в контексте текущего объекта global. Выполняемый код не имеет доступа к локальной области видимости, но имеет доступ к текущему объекту global.
В следующем примере компилируется код, который увеличивает переменную global, а затем выполняет этот код несколько раз:
Модули JavaScript
import { Script } from 'node:vm';
global.globalVar = 0;
const script = new Script('globalVar += 1', { filename: 'myfile.vm' });
for (let i = 0; i < 1000; ++i) {
script.runInThisContext();
}
console.log(globalVar);
// 1000
CommonJS
const { Script } = require('node:vm');
global.globalVar = 0;
const script = new Script('globalVar += 1', { filename: 'myfile.vm' });
for (let i = 0; i < 1000; ++i) {
script.runInThisContext();
}
console.log(globalVar);
// 1000
script.sourceMapURL
- Тип:
<string>|<undefined>
Если скрипт скомпилирован из исходного кода, содержащего специальный комментарий с картой исходного кода, этому свойству будет присвоен URL карты исходного кода.
Модули JavaScript
import vm from 'node:vm';
const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`);
console.log(script.sourceMapURL);
// Prints: sourcemap.json
CommonJS
const vm = require('node:vm');
const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`);
console.log(script.sourceMapURL);
// Prints: sourcemap.json
Класс: vm.Module
Стабильность: 1 — Экспериментальный
Эта функция доступна только при включённом флаге команды --experimental-vm-modules.
Класс vm.Module предоставляет низкоуровневый интерфейс для использования модулей ECMAScript в контекстах VM. Он является аналогом класса vm.Script и точно повторяет структуру записей модулей, определённых в спецификации ECMAScript.
Однако, в отличие от vm.Script, каждый объект vm.Module привязан к контексту с момента его создания.
Для использования объекта vm.Module требуются три отдельные шага: создание/разбор, связывание и вычисление. Эти три шага показаны в следующем примере.
Эта реализация находится на более низком уровне, чем загрузчик модулей ECMAScript. Взаимодействовать с загрузчиком пока невозможно, однако такая поддержка запланирована.
Модули JavaScript
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 rootModule = new vm.SourceTextModule(`
import s from 'foo';
s;
print(s);
`, { context: contextifiedObject });
// Step 2
//
// "Link" the imported dependencies of this Module to it.
//
// Obtain the requested dependencies of a SourceTextModule by
// `sourceTextModule.moduleRequests` and resolve them.
//
// Even top-level Modules without dependencies must be explicitly linked. The
// array passed to `sourceTextModule.linkRequests(modules)` can be
// empty, however.
//
// Note: This is a contrived example in that the resolveAndLinkDependencies
// 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.
const moduleMap = new Map([
['root', rootModule],
]);
function resolveAndLinkDependencies(module) {
const requestedModules = module.moduleRequests.map((request) => {
// In a full-fledged module system, the resolveAndLinkDependencies would
// resolve the module with the module cache key `[specifier, attributes]`.
// In this example, we just use the specifier as the key.
const specifier = request.specifier;
let requestedModule = moduleMap.get(specifier);
if (requestedModule === undefined) {
requestedModule = new vm.SourceTextModule(`
// The "secret" variable refers to the global variable we added to
// "contextifiedObject" when creating the context.
export default secret;
`, { context: module.context });
moduleMap.set(specifier, requestedModule);
// Resolve the dependencies of the new module as well.
resolveAndLinkDependencies(requestedModule);
}
return requestedModule;
});
module.linkRequests(requestedModules);
}
resolveAndLinkDependencies(rootModule);
rootModule.instantiate();
// Step 3
//
// Evaluate the Module. The evaluate() method returns a promise which will
// resolve after the module has finished evaluating.
// Prints 42.
await rootModule.evaluate();
CommonJS
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 rootModule = new vm.SourceTextModule(`
import s from 'foo';
s;
print(s);
`, { context: contextifiedObject });
// Step 2
//
// "Link" the imported dependencies of this Module to it.
//
// Obtain the requested dependencies of a SourceTextModule by
// `sourceTextModule.moduleRequests` and resolve them.
//
// Even top-level Modules without dependencies must be explicitly linked. The
// array passed to `sourceTextModule.linkRequests(modules)` can be
// empty, however.
//
// Note: This is a contrived example in that the resolveAndLinkDependencies
// 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.
const moduleMap = new Map([
['root', rootModule],
]);
function resolveAndLinkDependencies(module) {
const requestedModules = module.moduleRequests.map((request) => {
// In a full-fledged module system, the resolveAndLinkDependencies would
// resolve the module with the module cache key `[specifier, attributes]`.
// In this example, we just use the specifier as the key.
const specifier = request.specifier;
let requestedModule = moduleMap.get(specifier);
if (requestedModule === undefined) {
requestedModule = new vm.SourceTextModule(`
// The "secret" variable refers to the global variable we added to
// "contextifiedObject" when creating the context.
export default secret;
`, { context: module.context });
moduleMap.set(specifier, requestedModule);
// Resolve the dependencies of the new module as well.
resolveAndLinkDependencies(requestedModule);
}
return requestedModule;
});
module.linkRequests(requestedModules);
}
resolveAndLinkDependencies(rootModule);
rootModule.instantiate();
// Step 3
//
// Evaluate the Module. The evaluate() method returns a promise which will
// resolve after the module has finished evaluating.
// Prints 42.
await rootModule.evaluate();
})();
module.error
- Тип:
<any>
Если module.status имеет значение 'errored', это свойство содержит исключение, выброшенное модулем при вычислении. При любом другом состоянии обращение к этому свойству приведёт к выбросу исключения.
Значение undefined нельзя использовать в случаях, когда исключение не выброшено, поскольку его можно спутать с throw undefined;.
Соответствует полю [[EvaluationError]] записей циклических модулей в спецификации ECMAScript.
module.evaluate([options])
-
options<Object>-
timeout<integer>Задаёт количество миллисекунд для вычисления до остановки выполнения. Если выполнение прервано, будет выброшеноError. Значение должно быть строго положительным целым числом. -
breakOnSigint<boolean>Если заданоtrue, получениеSIGINT(Ctrl+C) остановит выполнение и приведёт к выбросуError. Обработчики события, ранее добавленные черезprocess.on('SIGINT'), отключаются на время выполнения скрипта, но продолжают работать после его завершения. По умолчанию:false.
-
- Возвращает:
<Promise>При успешном выполнении разрешается со значениемundefined.
Вычисляет модуль и его зависимости. Соответствует конкретному методу Evaluate() записей циклических модулей в спецификации ECMAScript.
Если модуль является vm.SourceTextModule, evaluate() необходимо вызвать после инстанцирования модуля; в противном случае evaluate() вернёт отклонённый промис.
Для vm.SourceTextModule промис, возвращаемый evaluate(), может быть выполнен синхронно или асинхронно:
- Если
vm.SourceTextModuleне содержитawaitверхнего уровня ни в самом модуле, ни в его зависимостях, промис будет выполнен синхронно после вычисления модуля и всех его зависимостей.- Если вычисление завершится успешно, промис будет синхронно разрешён со значением
undefined. - Если вычисление приведёт к исключению, промис будет синхронно отклонён с исключением, из-за которого вычисление завершилось неудачно; это то же исключение, что и
module.error.
- Если вычисление завершится успешно, промис будет синхронно разрешён со значением
- Если
vm.SourceTextModuleсодержитawaitверхнего уровня в самом модуле или в любой из его зависимостей, промис будет выполнен асинхронно после вычисления модуля и всех его зависимостей.- Если вычисление завершится успешно, промис будет асинхронно разрешён со значением
undefined. - Если вычисление приведёт к исключению, промис будет асинхронно отклонён с исключением, из-за которого вычисление завершилось неудачно.
- Если вычисление завершится успешно, промис будет асинхронно разрешён со значением
Если модуль является vm.SyntheticModule, evaluate() всегда возвращает промис, который выполняется синхронно. См. спецификацию метода Evaluate() для записи синтетического модуля:
- Если
evaluateCallback, переданный конструктору, синхронно выбрасывает исключение,evaluate()возвращает промис, который будет синхронно отклонён с этим исключением. - Если
evaluateCallbackне выбрасывает исключение,evaluate()возвращает промис, который будет синхронно разрешён со значениемundefined.
evaluateCallback объекта vm.SyntheticModule выполняется синхронно в рамках вызова evaluate(), а возвращаемое им значение отбрасывается. Это означает, что если evaluateCallback — асинхронная функция, промис, возвращаемый evaluate(), не будет отражать её асинхронное поведение, а любые отклонения асинхронного evaluateCallback будут потеряны.
evaluate() также можно вызвать повторно после вычисления модуля. В этом случае:
- Если начальное вычисление завершилось успешно (
module.statusимеет значение'evaluated'), метод ничего не сделает и вернёт промис, разрешающийся со значениемundefined. - Если начальное вычисление привело к исключению (
module.statusимеет значение'errored'), метод повторно отклонит промис с исключением, возникшим при начальном вычислении.
Этот метод нельзя вызывать во время вычисления модуля (module.status имеет значение 'evaluating').
module.identifier
- Тип:
<string>
Идентификатор текущего модуля, заданный в конструкторе.
module.link(linker)
-
linker<Function>-
specifier<string>Спецификатор запрашиваемого модуля:import foo from 'foo'; // ^^^^^ the module specifier mjscopy
-
referencingModule<vm.Module>ОбъектModule, для которого вызываетсяlink(). -
extra<Object> -
Возвращает:
<vm.Module>|<Promise>
-
- Возвращает:
<Promise>
Связывает зависимости модуля. Этот метод необходимо вызвать до оценки, и его можно вызвать только один раз для каждого модуля.
Используйте sourceTextModule.linkRequests(modules) и sourceTextModule.instantiate(), чтобы связывать модули синхронно или асинхронно.
Ожидается, что функция вернет объект Module или Promise, который в итоге разрешится в объект Module. Возвращенный Module должен удовлетворять следующим двум инвариантам:
- Он должен принадлежать тому же контексту, что и родительский
Module. - Его
statusне должен быть'errored'.
Если status возвращенного Module равен 'unlinked', этот метод будет рекурсивно вызван для возвращенного Module с той же предоставленной функцией linker.
link() возвращает Promise, который будет разрешен, когда все экземпляры, участвующие в связывании, разрешатся в корректный Module, или отклонен, если функция связывания выбросит исключение либо вернет некорректный Module.
Функция связывания приблизительно соответствует абстрактной операции HostResolveImportedModule, определяемой реализацией в спецификации ECMAScript, с несколькими важными отличиями:
- Функция связывания может быть асинхронной, тогда как HostResolveImportedModule является синхронной.
Фактическая реализация HostResolveImportedModule, используемая при связывании модулей, возвращает модули, связанные в процессе связывания. Поскольку к этому моменту все модули уже полностью связаны, согласно спецификации реализация HostResolveImportedModule полностью синхронна.
Соответствует полю конкретного метода Link() записей Cyclic Module Record в спецификации ECMAScript.
module.namespace
- Тип:
<Object>
Объект пространства имен модуля. Он доступен только после завершения связывания (module.link()).
Соответствует абстрактной операции GetModuleNamespace в спецификации ECMAScript.
module.status
- Тип:
<string>
Текущий статус модуля. Может принимать одно из следующих значений:
-
'unlinked': вызовmodule.link()еще не выполнялся. -
'linking': вызовmodule.link()выполнен, но еще не разрешились все промисы, возвращенные функцией связывания. -
'linked': модуль успешно связан, и все его зависимости связаны, но вызовmodule.evaluate()еще не выполнялся. -
'evaluating': модуль оценивается черезmodule.evaluate()для себя или родительского модуля. -
'evaluated': модуль успешно оценен. -
'errored': модуль оценен, но было выброшено исключение.
За исключением 'errored', эта строка статуса соответствует полю [[Status]] записи Cyclic Module Record в спецификации. 'errored' соответствует 'evaluated' в спецификации, но при этом [[EvaluationError]] имеет значение, отличное от undefined.
Класс: vm.SourceTextModule
Стабильность: 1 — экспериментальный
Эта функция доступна только при включенном флаге командной строки --experimental-vm-modules.
- Наследует:
<vm.Module>
Класс vm.SourceTextModule предоставляет запись модуля с исходным текстом, определенную в спецификации ECMAScript.
new vm.SourceTextModule(code[, options])
-
code<string>Код модуля JavaScript для разбора -
options-
identifier<string>Строка, используемая в трассировках стека. По умолчанию:'vm:module(i)', гдеi— возрастающий индекс, зависящий от контекста. -
cachedData<Buffer>|<TypedArray>|<DataView>Предоставляет необязательныйBufferилиTypedArrayлибоDataViewс данными кэша кода V8 для указанного исходного кода.codeдолжен совпадать с модулем, из которого был создан этотcachedData. -
context<Object>Контекстизированный объект, возвращенный методомvm.createContext(), в котором следует скомпилировать и оценить этотModule. Если контекст не указан, модуль оценивается в текущем контексте выполнения. -
lineOffset<integer>Задает смещение номера строки, отображаемое в трассировках стека, создаваемых этимModule. По умолчанию:0. -
columnOffset<integer>Задает смещение номера столбца в первой строке, отображаемое в трассировках стека, создаваемых этимModule. По умолчанию:0. -
initializeImportMeta<Function>Вызывается при оценке этогоModuleдля инициализацииimport.meta.-
meta<import.meta> -
module<vm.SourceTextModule>
-
-
importModuleDynamically<Function>Используется для указания способа загрузки модулей при оценке этого модуля во время вызоваimport(). Этот параметр относится к экспериментальному API модулей. Мы не рекомендуем использовать его в производственной среде. Подробные сведения см. в разделе Поддержка динамическогоimport()в API компиляции.
-
Создает новый экземпляр SourceTextModule.
Объекты, назначенные свойствам объекта import.meta, могут позволить модулю получать доступ к данным за пределами указанного context. Используйте vm.runInContext() для создания объектов в определенном контексте.
Модули JavaScript
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 = {};
},
});
// The module has an empty `moduleRequests` array.
module.linkRequests([]);
module.instantiate();
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);
CommonJS
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 = {};
},
});
// The module has an empty `moduleRequests` array.
module.linkRequests([]);
module.instantiate();
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>
Создает кэш кода, который можно использовать с параметром cachedData конструктора SourceTextModule. Возвращает Buffer. Этот метод можно вызывать любое количество раз до оценки модуля.
Кэш кода SourceTextModule не содержит состояний, наблюдаемых из JavaScript. Кэш кода безопасно сохранять вместе с исходным текстом скрипта и многократно использовать для создания новых экземпляров SourceTextModule.
Функции в исходном коде SourceTextModule можно пометить для отложенной компиляции; они не компилируются при создании SourceTextModule. Эти функции будут скомпилированы при первом вызове. Кэш кода сериализует метаданные, известные V8 на данный момент о SourceTextModule, которые V8 может использовать для ускорения последующих компиляций.
// 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 });
jscopy
sourceTextModule.dependencySpecifiers
Стабильность: 0 — устарело: используйте вместо него sourceTextModule.moduleRequests.
- Тип:
<string[]>
Спецификаторы всех зависимостей этого модуля. Возвращенный массив заморожен, чтобы запретить любые его изменения.
Соответствует полю [[RequestedModules]] записей Cyclic Module Record в спецификации ECMAScript.
sourceTextModule.hasAsyncGraph()
- Возвращает:
<boolean>
Обходит граф зависимостей и возвращает true, если какой-либо модуль в зависимостях или сам этот модуль содержит выражения await верхнего уровня; в противном случае возвращает false.
Поиск может выполняться медленно, если граф достаточно велик.
Для этого модуль сначала необходимо инстанцировать. Если модуль еще не инстанцирован, будет выброшена ошибка.
sourceTextModule.hasTopLevelAwait()
- Возвращает:
<boolean>
Возвращает сведения о том, содержит ли сам модуль выражения await верхнего уровня.
Это соответствует полю [[HasTLA]] в Cyclic Module Record спецификации ECMAScript.
sourceTextModule.instantiate()
- Возвращает:
<undefined>
Инстанцирует модуль с уже связанными запрашиваемыми модулями.
Этот метод разрешает импортированные привязки модуля, включая имена повторно экспортируемых привязок. Если имеются привязки, которые невозможно разрешить, синхронно выбрасывается ошибка.
Если запрашиваемые модули содержат циклические зависимости, метод sourceTextModule.linkRequests(modules) необходимо вызвать для всех модулей цикла до вызова этого метода.
sourceTextModule.linkRequests(modules)
-
modules<vm.Module[]>Массив объектовvm.Module, от которых зависит этот модуль. Порядок модулей в массиве соответствует порядкуsourceTextModule.moduleRequests. - Возвращает:
<undefined>
Связывает зависимости модуля. Этот метод необходимо вызвать до оценки, и его можно вызвать только один раз для каждого модуля.
Порядок экземпляров модулей в массиве modules должен соответствовать порядку разрешения sourceTextModule.moduleRequests. Если два запроса модуля имеют одинаковый спецификатор и атрибуты импорта, для них необходимо использовать один и тот же экземпляр модуля, иначе будет выброшен ERR_MODULE_LINK_MISMATCH. Например, при связывании запросов для этого модуля:
import foo from 'foo'; import source Foo from 'foo'; mjscopy
Массив modules должен содержать две ссылки на один и тот же экземпляр, поскольку два запроса модуля идентичны, но относятся к двум фазам.
Если у модуля нет зависимостей, массив modules может быть пустым.
Пользователи могут использовать sourceTextModule.moduleRequests для реализации определяемой хостом абстрактной операции HostLoadImportedModule в спецификации ECMAScript, а также использовать sourceTextModule.linkRequests() для вызова определенной спецификацией операции FinishLoadingImportedModule для модуля со всеми зависимостями одним пакетом.
Создатель SourceTextModule сам определяет, будет ли разрешение зависимостей синхронным или асинхронным.
После связывания каждого модуля в массиве modules вызовите sourceTextModule.instantiate().
sourceTextModule.moduleRequests
- Тип:
<ModuleRequest[]>Зависимости этого модуля.
Запрашиваемые зависимости импорта этого модуля. Возвращенный массив заморожен, чтобы запретить любые его изменения.
Например, для следующего исходного текста:
import foo from 'foo';
import fooAlias from 'foo';
import bar from './bar.js';
import withAttrs from '../with-attrs.ts' with { arbitraryAttr: 'attr-val' };
import source Module from 'wasm-mod.wasm';
mjscopy
Значение sourceTextModule.moduleRequests будет следующим:
[
{
specifier: 'foo',
attributes: {},
phase: 'evaluation',
},
{
specifier: 'foo',
attributes: {},
phase: 'evaluation',
},
{
specifier: './bar.js',
attributes: {},
phase: 'evaluation',
},
{
specifier: '../with-attrs.ts',
attributes: { arbitraryAttr: 'attr-val' },
phase: 'evaluation',
},
{
specifier: 'wasm-mod.wasm',
attributes: {},
phase: 'source',
},
];
jscopyКласс: vm.SyntheticModule
Стабильность: 1 — экспериментальный
Эта функция доступна только при включенном флаге командной строки --experimental-vm-modules.
- Наследует:
<vm.Module>
Класс vm.SyntheticModule предоставляет запись синтетического модуля, определенную в спецификации WebIDL. Синтетические модули предназначены для предоставления универсального интерфейса, позволяющего включать источники, отличные от JavaScript, в графы модулей ECMAScript.
Модули JavaScript
import { SyntheticModule } from 'node:vm';
const source = '{ "a": 1 }';
const syntheticModule = new SyntheticModule(['default'], function() {
const obj = JSON.parse(source);
this.setExport('default', obj);
});
// Use `syntheticModule` in linking
(async () => {
await syntheticModule.link(() => {});
await syntheticModule.evaluate();
console.log('Default export:', syntheticModule.namespace.default);
})();
CommonJS
const { SyntheticModule } = require('node:vm');
const source = '{ "a": 1 }';
const syntheticModule = new SyntheticModule(['default'], function() {
const obj = JSON.parse(source);
this.setExport('default', obj);
});
// Use `syntheticModule` in linking
(async () => {
await syntheticModule.link(() => {});
await syntheticModule.evaluate();
console.log('Default export:', syntheticModule.namespace.default);
})();
new vm.SyntheticModule(exportNames, evaluateCallback[, options])
-
exportNames<string[]>Массив имен, которые будут экспортированы из модуля. -
evaluateCallback<Function>Вызывается при оценке модуля. -
options
Создает новый экземпляр SyntheticModule.
Объекты, назначенные экспортам этого экземпляра, могут позволить импортирующим модулям получать доступ к данным за пределами указанного context. Используйте vm.runInContext() для создания объектов в определенном контексте.
syntheticModule.setExport(name, value)
-
name<string>Имя экспорта, которому нужно присвоить значение. -
value<any>Значение, присваиваемое экспорту.
Этот метод задает слоты привязок экспорта модуля, присваивая им указанное значение.
Модули JavaScript
import vm from 'node:vm';
const m = new vm.SyntheticModule(['x'], () => {
m.setExport('x', 1);
});
await m.evaluate();
assert.strictEqual(m.namespace.x, 1);
CommonJS
const vm = require('node:vm');
(async () => {
const m = new vm.SyntheticModule(['x'], () => {
m.setExport('x', 1);
});
await m.evaluate();
assert.strictEqual(m.namespace.x, 1);
})();
Тип: ModuleRequest
- Тип:
<Object>-
specifier<string>Спецификатор запрашиваемого модуля. -
attributes<Object>Значение"with", переданное в WithClause в ImportDeclaration, или пустой объект, если значение не было указано. -
phase<string>Фаза запрашиваемого модуля ("source"или"evaluation").
-
ModuleRequest представляет запрос на импорт модуля с указанными атрибутами импорта и фазой.
vm.compileFunction(code[, params[, options]])
-
code<string>Тело компилируемой функции. -
params<string[]>Массив строк, содержащий все параметры функции. -
options<Object>-
filename<string>Задает имя файла, используемое в трассировках стека, создаваемых этим скриптом. По умолчанию:''. -
lineOffset<number>Задает смещение номера строки, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию:0. -
columnOffset<number>Задает смещение номера столбца в первой строке, отображаемое в трассировках стека, создаваемых этим скриптом. По умолчанию:0. -
cachedData<Buffer>|<TypedArray>|<DataView>Предоставляет необязательныйBufferилиTypedArrayлибоDataViewс данными кэша кода V8 для указанного исходного кода. Он должен быть создан предыдущим вызовомvm.compileFunction()с теми жеcodeиparams. -
produceCachedData<boolean>Задает, нужно ли создавать новые данные кэша. По умолчанию:false. -
parsingContext<Object>Контекстизированный объект, в котором следует скомпилировать указанную функцию. -
contextExtensions<Object[]>Массив, содержащий набор расширений контекста (объектов, оборачивающих текущую область видимости), которые будут применены при компиляции. По умолчанию:[]. -
importModuleDynamically<Function>|<vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER>Используется для указания способа загрузки модулей при оценке этой функции во время вызоваimport(). Этот параметр относится к экспериментальному API модулей. Мы не рекомендуем использовать его в производственной среде. Подробные сведения см. в разделе Поддержка динамическогоimport()в API компиляции.
-
- Возвращает:
<Function>
Компилирует указанный код в предоставленный контекст (если контекст не задан, используется текущий) и возвращает его, обернутым в функцию с указанными params.
vm.constants
- Тип:
<Object>
Возвращает объект, содержащий часто используемые константы для операций VM.
vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER
Стабильность: 1.1 — активная разработка
Константа, которую можно использовать в качестве параметра importModuleDynamically для vm.Script и vm.compileFunction(), чтобы Node.js загружал запрашиваемый модуль с помощью загрузчика ESM по умолчанию из основного контекста.
Подробные сведения см. в разделе Поддержка динамического import() в API компиляции.
vm.createContext([contextObject[, options]])
-
contextObject<Object>|<vm.constants.DONT_CONTEXTIFY>|<undefined>Либоvm.constants.DONT_CONTEXTIFY, либо объект, который будет контекстуализирован. Еслиundefined, для обеспечения обратной совместимости будет создан пустой контекстуализированный объект. -
options<Object>-
name<string>Понятное для человека имя вновь созданного контекста. По умолчанию:'VM Context i', гдеi— возрастающий числовой индекс созданного контекста. -
origin<string>Источник, соответствующий вновь созданному контексту и используемый для отображения. Источник должен быть отформатирован как URL, но содержать только схему, хост и порт (если необходимо), подобно значению свойстваurl.originобъектаURL. В частности, эта строка не должна содержать завершающую косую черту, поскольку она обозначает путь. По умолчанию:''. -
codeGeneration<Object>-
strings<boolean>Если установлено значение false, любые вызовыevalили конструкторов функций (Function,GeneratorFunctionи т. д.) приведут к возникновениюEvalError. По умолчанию:true. -
wasm<boolean>Если установлено значение false, любая попытка скомпилировать модуль WebAssembly приведёт к возникновениюWebAssembly.CompileError. По умолчанию:true.
-
-
microtaskMode<string>Если установлено значениеafterEvaluate, микрозадачи (задачи, запланированные черезPromiseиasync function) будут выполняться сразу после выполнения скрипта с помощьюscript.runInContext(). В этом случае они включаются в областиtimeoutиbreakOnSigint. -
importModuleDynamically<Function>|<vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER>Используется для указания способа загрузки модулей, когда в этом контексте вызываетсяimport()без ссылающегося скрипта или модуля. Этот параметр является частью экспериментального API модулей. Мы не рекомендуем использовать его в производственной среде. Подробную информацию см. в разделе Поддержка динамическогоimport()в API компиляции.
-
- Возвращает: контекстуализированный объект
<Object>.
Если заданный contextObject является объектом, метод vm.createContext() подготовит этот объект и вернёт ссылку на него, чтобы его можно было использовать при вызове vm.runInContext() или script.runInContext(). В таких скриптах глобальный объект будет обёрнут объектом contextObject, сохраняя все существующие свойства и получая встроенные объекты и функции, имеющиеся у любого стандартного глобального объекта. За пределами скриптов, запускаемых модулем vm, глобальные переменные останутся неизменными.
Модули JavaScript
import { createContext, runInContext } from 'node:vm';
global.globalVar = 3;
const context = { globalVar: 1 };
createContext(context);
runInContext('globalVar *= 2;', context);
console.log(context);
// Prints: { globalVar: 2 }
console.log(global.globalVar);
// Prints: 3
CommonJS
const { createContext, runInContext } = require('node:vm');
global.globalVar = 3;
const context = { globalVar: 1 };
createContext(context);
runInContext('globalVar *= 2;', context);
console.log(context);
// Prints: { globalVar: 2 }
console.log(global.globalVar);
// Prints: 3
Если contextObject опущен (или явно передан как undefined), будет возвращён новый пустой контекстуализированный объект.
Когда глобальный объект в только что созданном контексте контекстуализирован, он имеет некоторые особенности по сравнению с обычными глобальными объектами. Например, его нельзя заморозить. Чтобы создать контекст без особенностей контекстуализации, передайте vm.constants.DONT_CONTEXTIFY в качестве аргумента contextObject. Подробности см. в документации к vm.constants.DONT_CONTEXTIFY.
Метод vm.createContext() в первую очередь полезен для создания единственного контекста, который можно использовать для запуска нескольких скриптов. Например, при эмуляции веб-браузера этот метод можно использовать для создания единственного контекста, представляющего глобальный объект окна, а затем запускать все теги <script> в этом контексте.
Указанные name и origin контекста доступны через API Inspector.
vm.isContext(object)
Возвращает true, если заданный объект object был контекстуализирован с помощью vm.createContext() или является глобальным объектом контекста, созданного с помощью vm.constants.DONT_CONTEXTIFY.
vm.measureMemory([options])
Стабильность: 1 — экспериментальный
Измеряет память, известную V8 и используемую всеми контекстами, известными текущей изолированной среде V8, либо основным контекстом.
-
options<Object>Необязательно.-
mode<string>Либо'summary', либо'detailed'. В режиме сводки будет возвращена только память, измеренная для основного контекста. В подробном режиме будет возвращена память, измеренная для всех контекстов, известных текущей изолированной среде V8. По умолчанию:'summary' -
execution<string>Либо'default', либо'eager'. При стандартном выполнении обещание не будет выполнено, пока не начнётся следующая запланированная сборка мусора; это может занять некоторое время (или она вообще не начнётся, если программа завершится до следующей сборки мусора). При немедленном выполнении сборка мусора будет запущена сразу для измерения памяти. По умолчанию:'default'
-
- Возвращает:
<Promise>Если измерение памяти выполнено успешно, обещание будет выполнено с объектом, содержащим информацию об использовании памяти. В противном случае оно будет отклонено с ошибкойERR_CONTEXT_NOT_INITIALIZED.
Формат объекта, с которым может быть выполнено возвращённое Promise, зависит от движка V8 и может изменяться от одной версии V8 к другой.
Возвращаемый результат отличается от статистики, предоставляемой v8.getHeapSpaceStatistics(): vm.measureMemory() измеряет память, доступную из каждого специфичного для V8 контекста в текущем экземпляре движка V8, тогда как результат v8.getHeapSpaceStatistics() измеряет память, занимаемую каждой областью кучи в текущем экземпляре V8.
Модули JavaScript
import { createContext, measureMemory } from 'node:vm';
// Measure the memory used by the main context.
measureMemory({ mode: 'summary' })
// This is the same as vm.measureMemory()
.then((result) => {
// The current format is:
// {
// total: { jsMemoryEstimate: 1601828, jsMemoryRange: [1601828, 5275288] },
// WebAssembly: { code: 0, metadata: 33962 },
// }
console.log(result);
});
const context = createContext({ a: 1 });
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:', context.a);
// {
// total: { jsMemoryEstimate: 1767100, jsMemoryRange: [1767100, 5440560] },
// WebAssembly: { code: 0, metadata: 33962 },
// current: { jsMemoryEstimate: 1601828, jsMemoryRange: [1601828, 5275288] },
// other: [{ jsMemoryEstimate: 165272, jsMemoryRange: [Array] }],
// }
console.log(result);
});
CommonJS
const { createContext, measureMemory } = require('node:vm');
// Measure the memory used by the main context.
measureMemory({ mode: 'summary' })
// This is the same as vm.measureMemory()
.then((result) => {
// The current format is:
// {
// total: { jsMemoryEstimate: 1601828, jsMemoryRange: [1601828, 5275288] },
// WebAssembly: { code: 0, metadata: 33962 },
// }
console.log(result);
});
const context = createContext({ a: 1 });
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:', context.a);
// {
// total: { jsMemoryEstimate: 1767100, jsMemoryRange: [1767100, 5440560] },
// WebAssembly: { code: 0, metadata: 33962 },
// current: { jsMemoryEstimate: 1601828, jsMemoryRange: [1601828, 5275288] },
// other: [{ jsMemoryEstimate: 165272, jsMemoryRange: [Array] }],
// }
console.log(result);
});
vm.runInContext(code, contextifiedObject[, options])
-
code<string>Код JavaScript для компиляции и запуска. -
contextifiedObject<Object>Контекстуализированный объект, который будет использоваться в качествеglobalпри компиляции и запускеcode. -
options<Object>|<string>-
filename<string>Указывает имя файла, используемое в трассировках стека, созданных этим скриптом. По умолчанию:'evalmachine.<anonymous>'. -
lineOffset<number>Указывает смещение номера строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию:0. -
columnOffset<number>Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию:0. -
displayErrors<boolean>Еслиtrue, при возникновенииErrorво время компиляцииcodeстрока кода, вызвавшая ошибку, добавляется в трассировку стека. По умолчанию:true. -
timeout<integer>Указывает количество миллисекунд, в течение которых выполняетсяcodeдо завершения выполнения. Если выполнение будет завершено принудительно, будет выброшена ошибкаError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<boolean>Еслиtrue, получениеSIGINT(Ctrl+C) завершит выполнение и выбросит ошибкуError. Существующие обработчики события, добавленные с помощьюprocess.on('SIGINT'), отключаются на время выполнения скрипта, но продолжают работать после его завершения. По умолчанию:false. -
cachedData<Buffer>|<TypedArray>|<DataView>Предоставляет необязательныйBufferилиTypedArray, либоDataViewс данными кэша кода V8 для указанного исходного кода. -
importModuleDynamically<Function>|<vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER>Используется для указания способа загрузки модулей при вычислении этого скрипта, когда вызываетсяimport(). Этот параметр является частью экспериментального API модулей. Мы не рекомендуем использовать его в производственной среде. Подробную информацию см. в разделе Поддержка динамическогоimport()в API компиляции.
-
Метод vm.runInContext() компилирует code, выполняет его в контексте contextifiedObject, а затем возвращает результат. Выполняемый код не имеет доступа к локальной области видимости. Объект contextifiedObject должен быть предварительно контекстуализирован с помощью метода vm.createContext().
Если options является строкой, она указывает имя файла.
В следующем примере скомпилированы и выполнены разные скрипты с использованием одного контекстуализированного объекта:
Модули JavaScript
import { createContext, runInContext } from 'node:vm';
const contextObject = { globalVar: 1 };
createContext(contextObject);
for (let i = 0; i < 10; ++i) {
runInContext('globalVar *= 2;', contextObject);
}
console.log(contextObject);
// Prints: { globalVar: 1024 }
CommonJS
const { createContext, runInContext } = require('node:vm');
const contextObject = { globalVar: 1 };
createContext(contextObject);
for (let i = 0; i < 10; ++i) {
runInContext('globalVar *= 2;', contextObject);
}
console.log(contextObject);
// Prints: { globalVar: 1024 }
vm.runInNewContext(code[, contextObject[, options]])
-
code<string>Код JavaScript для компиляции и запуска. -
contextObject<Object>|<vm.constants.DONT_CONTEXTIFY>|<undefined>Либоvm.constants.DONT_CONTEXTIFY, либо объект, который будет контекстуализирован. Еслиundefined, для обеспечения обратной совместимости будет создан пустой контекстуализированный объект. -
options<Object>|<string>-
filename<string>Указывает имя файла, используемое в трассировках стека, созданных этим скриптом. По умолчанию:'evalmachine.<anonymous>'. -
lineOffset<number>Указывает смещение номера строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию:0. -
columnOffset<number>Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию:0. -
displayErrors<boolean>Еслиtrue, при возникновенииErrorво время компиляцииcodeстрока кода, вызвавшая ошибку, добавляется в трассировку стека. По умолчанию: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>-
strings<boolean>Если установлено значение false, любые вызовыevalили конструкторов функций (Function,GeneratorFunctionи т. д.) приведут к возникновениюEvalError. По умолчанию:true. -
wasm<boolean>Если установлено значение false, любая попытка скомпилировать модуль WebAssembly приведёт к возникновениюWebAssembly.CompileError. По умолчанию:true.
-
-
cachedData<Buffer>|<TypedArray>|<DataView>Предоставляет необязательныйBufferилиTypedArray, либоDataViewс данными кэша кода V8 для указанного исходного кода. -
importModuleDynamically<Function>|<vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER>Используется для указания способа загрузки модулей при вычислении этого скрипта, когда вызываетсяimport(). Этот параметр является частью экспериментального API модулей. Мы не рекомендуем использовать его в производственной среде. Подробную информацию см. в разделе Поддержка динамическогоimport()в API компиляции. -
microtaskMode<string>Если установлено значениеafterEvaluate, микрозадачи (задачи, запланированные черезPromiseиasync function) будут выполнены сразу после запуска скрипта. В этом случае они включаются в областиtimeoutиbreakOnSigint.
-
- Возвращает:
<any>результат последнего оператора, выполненного в скрипте.
Этот метод является сокращённой формой (new vm.Script(code, options)).runInContext(vm.createContext(options), options). Если options является строкой, она указывает имя файла.
Он выполняет несколько действий одновременно:
- Создаёт новый контекст.
- Если
contextObjectявляется объектом, контекстуализирует его в новом контексте. ЕслиcontextObjectне определён, создаёт новый объект и контекстуализирует его. ЕслиcontextObject—vm.constants.DONT_CONTEXTIFY, ничего не контекстуализирует. - Компилирует код как
vm.Script - Запускает скомпилированный код в созданном контексте. Код не имеет доступа к области видимости, в которой вызывается этот метод.
- Возвращает результат.
В следующем примере компилируется и выполняется код, который увеличивает глобальную переменную и задаёт новую. Эти глобальные переменные находятся в contextObject.
Модули JavaScript
import { runInNewContext, constants } from 'node:vm';
const contextObject = {
animal: 'cat',
count: 2,
};
runInNewContext('count += 1; name = "kitty"', contextObject);
console.log(contextObject);
// Prints: { animal: 'cat', count: 3, name: 'kitty' }
// This would throw if the context is created from a contextified object.
// vm.constants.DONT_CONTEXTIFY allows creating contexts with ordinary global objects that
// can be frozen.
const frozenContext = runInNewContext(
'Object.freeze(globalThis); globalThis;',
constants.DONT_CONTEXTIFY,
);
CommonJS
const { runInNewContext, constants } = require('node:vm');
const contextObject = {
animal: 'cat',
count: 2,
};
runInNewContext('count += 1; name = "kitty"', contextObject);
console.log(contextObject);
// Prints: { animal: 'cat', count: 3, name: 'kitty' }
// This would throw if the context is created from a contextified object.
// vm.constants.DONT_CONTEXTIFY allows creating contexts with ordinary global objects that
// can be frozen.
const frozenContext = runInNewContext(
'Object.freeze(globalThis); globalThis;',
constants.DONT_CONTEXTIFY,
);
vm.runInThisContext(code[, options])
-
code<string>Код JavaScript для компиляции и выполнения. -
options<Object>|<string>-
filename<string>Задает имя файла, используемое в трассировках стека, созданных этим скриптом. По умолчанию:'evalmachine.<anonymous>'. -
lineOffset<number>Задает смещение номера строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию:0. -
columnOffset<number>Задает смещение номера столбца первой строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию: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>Предоставляет необязательныйBufferилиTypedArrayлибоDataViewс данными кэша кода V8 для указанного исходного кода. -
importModuleDynamically<Function>|<vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER>Используется для указания способа загрузки модулей при вычислении этого скрипта, если вызываетсяimport(). Этот параметр относится к экспериментальному API модулей. Мы не рекомендуем использовать его в производственной среде. Подробную информацию см. в разделе Поддержка динамическогоimport()в API компиляции.
-
- Возвращает:
<any>результат выполнения последнего оператора в скрипте.
vm.runInThisContext() компилирует code, выполняет его в контексте текущего global и возвращает результат. Выполняемый код не имеет доступа к локальной области видимости, но имеет доступ к текущему объекту global.
Если options — строка, она задает имя файла.
В следующем примере показано использование vm.runInThisContext() и функции JavaScript eval() для выполнения одного и того же кода:
Модули JavaScript
import { runInThisContext } from 'node:vm';
let localVar = 'initial value';
const vmResult = 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'
CommonJS
const { runInThisContext } = require('node:vm');
let localVar = 'initial value';
const vmResult = 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').
Пример: запуск HTTP-сервера в виртуальной машине
При использовании script.runInThisContext() или vm.runInThisContext() код выполняется в текущем глобальном контексте V8. Код, переданный этому контексту виртуальной машины, будет иметь собственную изолированную область видимости.
Чтобы запустить простой веб-сервер с помощью модуля node:http, код, переданный контексту, должен самостоятельно вызвать require('node:http') либо получить ссылку на модуль node:http. Например:
Модули JavaScript
import { runInThisContext } from 'node:vm';
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const code = `
((require) => {
const { createServer } = require('node: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/');
})`;
runInThisContext(code)(require);
CommonJS
const { runInThisContext } = require('node:vm');
const code = `
((require) => {
const { createServer } = require('node: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/');
})`;
runInThisContext(code)(require);
В данном случае require() использует общее состояние с контекстом, из которого он передан. Это может привести к рискам при выполнении недоверенного кода, например к нежелательному изменению объектов в контексте.
Что значит «контекстизировать» объект?
Весь код JavaScript, выполняемый в Node.js, запускается в области видимости «контекста». Согласно руководству по встраиванию V8:
В V8 контекст — это среда выполнения, которая позволяет запускать отдельные, не связанные друг с другом приложения JavaScript в одном экземпляре V8. Необходимо явно указать контекст, в котором должен выполняться код JavaScript.
При вызове метода vm.createContext() с объектом аргумент contextObject используется для обертки глобального объекта нового экземпляра контекста V8 (если contextObject равно undefined, новый объект будет создан на основе текущего контекста до его контекстизации). Этот контекст V8 предоставляет code возможность выполняться с помощью методов модуля node:vm в изолированной глобальной среде. Процесс создания контекста V8 и связывания его с contextObject во внешнем контексте в этом документе называется «контекстизацией» объекта.
Контекстизация приводит к некоторым особенностям значения globalThis в контексте. Например, его нельзя заморозить, и оно не идентично по ссылке значению contextObject во внешнем контексте.
Модули JavaScript
import { createContext, runInContext } from 'node:vm';
// An undefined `contextObject` option makes the global object contextified.
const context = createContext();
console.log(runInContext('globalThis', context) === context); // false
// A contextified global object cannot be frozen.
try {
runInContext('Object.freeze(globalThis);', context);
} catch (e) {
console.log(`${e.constructor.name}: ${e.message}`); // TypeError: Cannot freeze
}
console.log(runInContext('globalThis.foo = 1; foo;', context)); // 1
CommonJS
const { createContext, runInContext } = require('node:vm');
// An undefined `contextObject` option makes the global object contextified.
const context = createContext();
console.log(runInContext('globalThis', context) === context); // false
// A contextified global object cannot be frozen.
try {
runInContext('Object.freeze(globalThis);', context);
} catch (e) {
console.log(`${e.constructor.name}: ${e.message}`); // TypeError: Cannot freeze
}
console.log(runInContext('globalThis.foo = 1; foo;', context)); // 1
Чтобы создать контекст с обычным глобальным объектом и получить доступ к глобальному прокси во внешнем контексте с меньшим количеством особенностей, укажите vm.constants.DONT_CONTEXTIFY в качестве аргумента contextObject.
vm.constants.DONT_CONTEXTIFY
Эта константа, используемая в качестве аргумента contextObject в API vm, указывает Node.js создать контекст, не оборачивая его глобальный объект другим объектом специальным для Node.js способом. В результате значение globalThis в новом контексте будет вести себя ближе к обычному.
Модули JavaScript
import { createContext, runInContext, constants } from 'node:vm';
// Use vm.constants.DONT_CONTEXTIFY to freeze the global object.
const context = createContext(constants.DONT_CONTEXTIFY);
runInContext('Object.freeze(globalThis);', context);
try {
runInContext('bar = 1; bar;', context);
} catch (e) {
console.log(`${e.constructor.name}: ${e.message}`); // ReferenceError: bar is not defined
}
CommonJS
const { createContext, runInContext, constants } = require('node:vm');
// Use vm.constants.DONT_CONTEXTIFY to freeze the global object.
const context = createContext(constants.DONT_CONTEXTIFY);
runInContext('Object.freeze(globalThis);', context);
try {
runInContext('bar = 1; bar;', context);
} catch (e) {
console.log(`${e.constructor.name}: ${e.message}`); // ReferenceError: bar is not defined
}
Если vm.constants.DONT_CONTEXTIFY используется в качестве аргумента contextObject для vm.createContext(), возвращаемый объект представляет собой объект, похожий на прокси глобального объекта в только что созданном контексте, с меньшим количеством особенностей, специфичных для Node.js. Он идентичен по ссылке значению globalThis в новом контексте, может изменяться извне контекста и позволяет напрямую обращаться к встроенным объектам нового контекста.
Модули JavaScript
import { createContext, runInContext, constants } from 'node:vm';
const context = createContext(constants.DONT_CONTEXTIFY);
// Returned object is reference equal to globalThis in the new context.
console.log(runInContext('globalThis', context) === context); // true
// Can be used to access globals in the new context directly.
console.log(context.Array); // [Function: Array]
runInContext('foo = 1;', context);
console.log(context.foo); // 1
context.bar = 1;
console.log(runInContext('bar;', context)); // 1
// Can be frozen and it affects the inner context.
Object.freeze(context);
try {
runInContext('baz = 1; baz;', context);
} catch (e) {
console.log(`${e.constructor.name}: ${e.message}`); // ReferenceError: baz is not defined
}
CommonJS
const { createContext, runInContext, constants } = require('node:vm');
const context = createContext(constants.DONT_CONTEXTIFY);
// Returned object is reference equal to globalThis in the new context.
console.log(runInContext('globalThis', context) === context); // true
// Can be used to access globals in the new context directly.
console.log(context.Array); // [Function: Array]
runInContext('foo = 1;', context);
console.log(context.foo); // 1
context.bar = 1;
console.log(runInContext('bar;', context)); // 1
// Can be frozen and it affects the inner context.
Object.freeze(context);
try {
runInContext('baz = 1; baz;', context);
} catch (e) {
console.log(`${e.constructor.name}: ${e.message}`); // ReferenceError: baz is not defined
}
Взаимодействие тайм-аута с асинхронными задачами и промисами
Promise и async function могут планировать задачи, асинхронно выполняемые движком JavaScript. По умолчанию эти задачи выполняются после завершения всех функций JavaScript в текущем стеке. Это позволяет обойти действие параметров timeout и breakOnSigint.
Например, следующий код, выполняемый с помощью vm.runInNewContext() с тайм-аутом в 5 миллисекунд, планирует бесконечный цикл, который запустится после разрешения промиса. Запланированный цикл не прерывается тайм-аутом:
Модули JavaScript
import { runInNewContext } from 'node:vm';
function loop() {
console.log('entering loop');
while (1) console.log(Date.now());
}
runInNewContext(
'Promise.resolve().then(() => loop());',
{ loop, console },
{ timeout: 5 },
);
// This is printed *before* 'entering infinite loop' (!)
console.log('done executing');
CommonJS
const { runInNewContext } = require('node:vm');
function loop() {
console.log('entering loop');
while (1) console.log(Date.now());
}
runInNewContext(
'Promise.resolve().then(() => loop());',
{ loop, console },
{ timeout: 5 },
);
// This is printed *before* 'entering infinite loop' (!)
console.log('done executing');
Эту проблему можно решить, передав microtaskMode: 'afterEvaluate' коду, который создает Context:
Модули JavaScript
import { runInNewContext } from 'node:vm';
function loop() {
while (1) console.log(Date.now());
}
runInNewContext(
'Promise.resolve().then(() => loop());',
{ loop, console },
{ timeout: 5, microtaskMode: 'afterEvaluate' },
);
CommonJS
const { runInNewContext } = require('node:vm');
function loop() {
while (1) console.log(Date.now());
}
runInNewContext(
'Promise.resolve().then(() => loop());',
{ loop, console },
{ timeout: 5, microtaskMode: 'afterEvaluate' },
);
В этом случае микрозадача, запланированная через promise.then(), будет выполнена до возврата из vm.runInNewContext() и прервана механизмом timeout. Это относится только к коду, выполняемому в vm.Context, поэтому, например, vm.runInThisContext() не принимает этот параметр.
Обработчики промисов помещаются в очередь микрозадач контекста, в котором они были созданы. Например, если в приведенном выше примере заменить () => loop() просто на loop, то loop будет помещен в глобальную очередь микрозадач, поскольку это функция из внешнего (основного) контекста, и поэтому она также сможет обойти тайм-аут.
Если асинхронные функции планирования, такие как process.nextTick(), queueMicrotask(), setTimeout(), setImmediate() и т. д., доступны внутри vm.Context, переданные им функции будут добавлены в глобальные очереди, общие для всех контекстов. Поэтому тайм-аут также не контролирует обратные вызовы, переданные этим функциям.
Если microtaskMode имеет значение 'afterEvaluate', будьте осторожны при совместном использовании промисов между контекстами
В режиме 'afterEvaluate' у Context есть собственная очередь микрозадач, отдельная от глобальной очереди микрозадач внешнего (основного) контекста. Хотя этот режим необходим для обеспечения timeout и поддержки breakOnSigint с асинхронными задачами, он также затрудняет совместное использование промисов между контекстами.
В приведенном ниже примере промис создается во внутреннем контексте и передается во внешний. Когда внешний контекст вызывает await для этого промиса, поток выполнения внешнего контекста неожиданным образом прерывается: инструкция log так и не выполняется.
Модули JavaScript
import { createContext, runInContext } from 'node:vm';
const inner_context = createContext({}, { microtaskMode: 'afterEvaluate' });
// runInContext() returns a Promise created in the inner context.
const inner_promise = runInContext('Promise.resolve()', inner_context);
// As part of performing `await`, the JavaScript runtime must enqueue a task
// on the microtask queue of the context where `inner_promise` was created.
// A task is added on the inner microtask queue, but **it will not be run
// automatically**: this task will remain pending indefinitely.
//
// Since the outer microtask queue is empty, execution in the outer module
// falls through, and the log statement below is never executed.
await inner_promise;
console.log('this will NOT be printed');
CommonJS
const { createContext, runInContext } = require('node:vm');
// runInContext() returns a Promise created in the inner context.
const inner_context = createContext({}, { microtaskMode: 'afterEvaluate' });
(async () => {
const inner_promise = runInContext('Promise.resolve()', inner_context);
// As part of performing `await`, the JavaScript runtime must enqueue a task
// on the microtask queue of the context where `inner_promise` was created.
// A task is added on the inner microtask queue, but **it will not be run
// automatically**: this task will remain pending indefinitely.
//
// Since the outer microtask queue is empty, execution in the outer module
// falls through, and the log statement below is never executed.
await inner_promise;
console.log('this will NOT be printed');
})();
Чтобы успешно совместно использовать промисы между контекстами с разными очередями микрозадач, необходимо обеспечить выполнение задач во внутренней очереди микрозадач каждый раз, когда внешний контекст добавляет задачу во внутреннюю очередь микрозадач.
Задачи в очереди микрозадач заданного контекста выполняются при каждом вызове runInContext() или SourceTextModule.evaluate() для скрипта или модуля, использующего этот контекст. В нашем примере обычный поток выполнения можно восстановить, запланировав второй вызов runInContext() до await inner_promise.
// Schedule `runInContext()` to manually drain the inner context microtask
// queue; it will run after the `await` statement below.
setImmediate(() => {
vm.runInContext('', context);
});
await inner_promise;
console.log('OK');
mjscopy
Примечание: Строго говоря, в этом режиме node:vm отступает от буквального требования спецификации ECMAScript о постановке задач в очередь, позволяя асинхронным задачам из разных контекстов выполняться в порядке, отличном от порядка их постановки в очередь.
Поддержка динамического import() в API компиляции
Следующие API поддерживают параметр importModuleDynamically для включения динамического import() в коде, скомпилированном модулем vm.
new vm.Scriptvm.compileFunction()new vm.SourceTextModulevm.runInThisContext()vm.runInContext()vm.runInNewContext()vm.createContext()
Этот параметр по-прежнему относится к экспериментальному API модулей. Мы не рекомендуем использовать его в производственной среде.
Если параметр importModuleDynamically не указан или имеет значение undefined
Если этот параметр не указан или имеет значение undefined, код, содержащий import(), все еще можно скомпилировать с помощью API vm, но при выполнении скомпилированного кода и фактическом вызове import() результат будет отклонен с ошибкой ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING.
Если importModuleDynamically имеет значение vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER
В настоящее время этот параметр не поддерживается для vm.SourceTextModule.
При использовании этого параметра, когда в скомпилированном коде инициируется import(), Node.js использует загрузчик ESM по умолчанию из основного контекста, чтобы загрузить запрошенный модуль и передать его выполняемому коду.
Это дает компилируемому коду доступ к встроенным модулям Node.js, таким как fs или http. Если код выполняется в другом контексте, учитывайте, что объекты, созданные модулями, загруженными из основного контекста, по-прежнему относятся к основному контексту, а не к встроенным классам instanceof в новом контексте.
CommonJS
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);
Модули JavaScript
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);
Этот параметр также позволяет скрипту или функции загружать пользовательские модули:
Модули JavaScript
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);
CommonJS
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);
При загрузке пользовательских модулей с помощью загрузчика по умолчанию из основного контекста следует учитывать несколько особенностей:
- Модуль будет разрешен относительно параметра
filename, переданного вvm.Scriptилиvm.compileFunction(). Разрешение может выполняться относительноfilename, являющегося абсолютным путем или строкой URL. Еслиfilename— строка, которая не является ни абсолютным путем, ни URL, либо если он не задан, разрешение будет выполняться относительно текущего рабочего каталога процесса. В случаеvm.createContext()разрешение всегда выполняется относительно текущего рабочего каталога, поскольку этот параметр используется только тогда, когда отсутствует ссылающийся скрипт или модуль. - Для каждого
filename, разрешающегося в определенный путь, результат может быть кэширован после первой успешной загрузки процесса из этого пути, а последующая загрузка того же модуля из того же пути вернет тот же результат. Еслиfilename— строка URL, кэш не будет использоваться, если параметры поиска в URL различаются. Дляfilename, не являющихся строками URL, в настоящее время нет способа обойти кэширование.
Если 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>Ссылающийся объект — это скомпилированныйvm.Scriptдляnew vm.Script,vm.runInThisContext,vm.runInContextиvm.runInNewContext. Дляvm.compileFunctionэто скомпилированныйFunction, дляnew vm.SourceTextModule— скомпилированныйvm.SourceTextModule, а дляvm.createContext()— контекстObject. -
importAttributes<Object>Значение"with", переданное необязательному параметруoptionsExpression, либо пустой объект, если значение не было передано. -
phase<string>Фаза динамического импорта ("source"или"evaluation"). - Возвращает:
<Module Namespace Object>|<vm.Module>Рекомендуется возвращатьvm.Module, чтобы воспользоваться отслеживанием ошибок и избежать проблем с пространствами имен, содержащими экспорты функцийthen.
Модули JavaScript
// 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' } }
CommonJS
// 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' } }
})();
© Joyent, Inc. and other Node contributors
Licensed under the MIT License.
Node.js is a trademark of Joyent, Inc. and is used with its permission.
We are not endorsed by or affiliated with Joyent.
https://nodejs.org/api/vm.html