Виртуальная машина (выполнение 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. copy Класс: vm.Script
Экземпляры класса vm.Script содержат предварительно скомпилированные скрипты, которые могут быть выполнены в определённых контекстах.
new vm.Script(code[, options])
-
code<строка> Код JavaScript для компиляции. -
options<Объект> | <строка>-
filename<строка> Указывает имя файла, используемое в отладке стека, созданном этим скриптом. По умолчанию:'evalmachine.<anonymous>'. -
lineOffset<число> Указывает смещение номера строки, отображаемое в отладке стека, созданной этим скриптом. По умолчанию:0. -
columnOffset<число> Указывает смещение номера столбца первой строки, отображаемое в отладке стека, созданной этим скриптом. По умолчанию:0. -
cachedData<Буфер> | <Тип массива> | <DataView> Предоставляет необязательныйBufferилиTypedArray, илиDataViewс данными кэша кода V8 для предоставленного исходного кода. При предоставлении значениеcachedDataRejectedбудет установлено вtrueилиfalseв зависимости от принятия данных V8. -
produceCachedData<логическое значение> Когдаtrueи нетcachedData, V8 попытается создать данные кэша кода дляcode. При успехе будет созданBufferс данными кэша кода V8 и сохранён в свойствеcachedDataэкземпляра возвращённогоvm.Script. ЗначениеcachedDataProducedбудет установлено вtrueилиfalseв зависимости от успешности создания данных кэша кода. Этот параметр устарел в пользуscript.createCachedData(). По умолчанию:false. -
importModuleDynamically<Функция> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей во время оценки этого скрипта при вызовеimport(). Этот параметр является частью экспериментального API модулей. Мы не рекомендуем его использовать в рабочей среде. Для получения подробной информации см. Поддержка динамическогоimport()в API компиляции.
-
Если options является строкой, то она указывает имя файла.
Создание нового объекта vm.Script компилирует code, но не запускает его. Скомпилированный vm.Script может быть запущен несколько раз позже. code не привязан к какому-либо глобальному объекту, а привязывается перед каждым запуском только для этого запуска.
script.cachedDataRejected
Когда cachedData предоставляется для создания vm.Script, это значение будет установлено в true или false в зависимости от принятия данных V8. В противном случае значение равно undefined.
script.createCachedData()
- Возвращает: <Буфер>
Создаёт кэш кода, который можно использовать с параметром 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()`. copy
script.runInContext(contextifiedObject[, options])
-
contextifiedObject<Объект> Объект, контекстированный с помощью методаvm.createContext(). -
options<Объект>-
displayErrors<логическое значение> Приtrue, если при компиляцииcodeвозникает ошибкаError, строка кода, вызвавшая ошибку, будет добавлена в отладочный стек. По умолчанию:true. -
timeout<целое число> Указывает количество миллисекунд для выполненияcodeперед завершением выполнения. Если выполнение завершится, будет выброшена ошибкаError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<логическое значение> Еслиtrue, получениеSIGINT(клавиши Ctrl + C) завершит выполнение и выбросит ошибкуError. Существующие обработчики события, добавленные с помощьюprocess.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работать после него. По умолчанию:false.
-
- Возвращает: <любое> результат последнего оператора, выполненного в скрипте.
Выполняет скомпилированный код, содержащийся в объекте vm.Script, внутри заданного contextifiedObject и возвращает результат. Выполняемый код не имеет доступа к локальному пространству имён.
В следующем примере компилируется код, который увеличивает глобальную переменную, устанавливает значение другой глобальной переменной, а затем выполняет код несколько раз. Глобальные переменные содержатся в объекте 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' } copy Использование параметров 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<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> результат последнего выполненного оператора в скрипте.
Сначала контекстуализирует переданный 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' }] copy
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, а затем выполняется несколько раз:
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 copy
script.sourceMapURL
Если скрипт скомпилирован из источника, содержащего магический комментарий карты исходного кода, это свойство будет установлено в URL карты исходного кода.
Модули MJS
import vm from 'node:vm';
const script = new vm.Script(`
function myFunc() {}
//# sourceMappingURL=sourcemap.json
`);
console.log(script.sourceMapURL);
// Prints: sourcemap.json
Модули CJS
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 предоставляет низкоуровневый интерфейс для использования модулей ECMAScript в контекстах VM. Он является аналогом класса vm.Script, который тесно соответствует Записи модуля, как определено в спецификации ECMAScript.
В отличие от vm.Script, каждый объект vm.Module привязан к контексту с момента создания. Операции с объектами vm.Module по своей природе асинхронны, в отличие от синхронного характера объектов vm.Script. Использование функций 'async' может помочь с манипулированием объектами vm.Module.
Использование объекта vm.Module требует трех четко выделенных этапов: создание/парсинг, связывание и оценка. Эти три этапа проиллюстрированы в следующем примере.
Эта реализация находится на более низком уровне, чем загрузчик модулей ECMAScript. Пока нет возможности взаимодействовать с Загрузчиком, хотя поддержка планируется.
Модули MJS
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();
Модули CJS
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
Спецификаторы всех зависимостей этого модуля. Возвращаемый массив заморожен, чтобы запретить любые изменения в нем.
Соответствует полю [[RequestedModules]] записей циклических модулей в спецификации ECMAScript.
module.error
Если статус module.status 'errored', это свойство содержит исключение, сгенерированное модулем во время оценки. Если статус другой, доступ к этому свойству приведет к сгенерированному исключению.
Значение undefined нельзя использовать в случаях, когда исключение не было сгенерировано, из-за возможной неоднозначности с throw undefined;.
Соответствует полю [[EvaluationError]] записей циклических модулей в спецификации ECMAScript.
module.evaluate([options])
-
options<Объект>-
timeout<целое число> Указывает количество миллисекунд для оценки перед завершением выполнения. Если выполнение прервано, будет брошено исключениеError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<логическое значение> Еслиtrue, получениеSIGINT(Ctrl+C) завершит выполнение и сгенерирует исключениеError. Существующие обработчики события, присоединенные с помощьюprocess.on('SIGINT'), будут отключены во время выполнения сценария, но продолжат работать после него. По умолчанию:false.
-
- Возвращает: <Promise> Успешно выполняется с
undefinedпри успешном завершении.
Оценить модуль.
Это необходимо вызвать после того, как модуль был связан; иначе он будет отклонен. Его можно также вызвать, когда модуль уже был оценен, в этом случае он либо ничего не сделает, если начальная оценка завершилась успешно (module.status является 'evaluated'), либо повторно сгенерирует исключение, к которому привела начальная оценка (module.status является 'errored').
Этот метод нельзя вызывать, пока модуль оценивается (module.status равно 'evaluating').
Соответствует полю Evaluate() конкретного метода записей циклических модулей в спецификации ECMAScript.
module.identifier
Идентификатор текущего модуля, заданный в конструкторе.
module.link(linker)
-
linker<Функция>-
specifier<строка> Спецификатор запрашиваемого модуля:import foo from 'foo'; // ^^^^^ the module specifier copy
-
referencingModule<vm.Модуль> ОбъектModule, на котором вызываетсяlink(). -
extra<Объект> -
Возвращает: <vm.Модуль> | <Promise>
-
- Возвращает: <Promise>
Связать зависимости модуля. Этот метод должен быть вызван перед оценкой и может быть вызван только один раз на модуль.
Функция должна вернуть объект Module или Promise, который в конечном итоге разрешается в объект Module. Возвращенный Module должен удовлетворять следующим двум инвариантам:
- Он должен принадлежать к тому же контексту, что и родительский
Module. - Его
statusне должен быть'errored'.
Если Module возвращенного объекта status равен 'unlinked', этот метод будет рекурсивно вызван для возвращенного Module с той же предоставленной функцией linker.
link() возвращает Promise, который будет разрешен, когда все экземпляры связывания разрешатся в допустимый объект Module, или отклонен, если функция связывателя сгенерирует исключение или вернет недопустимый Module.
Функция связывателя в общих чертах соответствует определенной реализацией абстрактной операции HostResolveImportedModule в спецификации ECMAScript, с некоторыми ключевыми отличиями:
- Функция связывателя может быть асинхронной, в то время как HostResolveImportedModule синхронна.
Фактическая реализация HostResolveImportedModule, используемая при связывании модулей, возвращает модули, связанные во время связывания. Поскольку на этом этапе все модули уже будут полностью связаны, реализация HostResolveImportedModule является полностью синхронной, в соответствии со спецификацией.
Соответствует полю Link() конкретного метода записей циклических модулей в спецификации ECMAScript.
module.namespace
Объект пространства имен модуля. Он доступен только после завершения связывания (module.link()).
Соответствует абстрактной операции GetModuleNamespace в спецификации ECMAScript.
module.status
Текущий статус модуля. Может быть одним из следующих:
-
'unlinked': Методmodule.link()еще не был вызван. -
'linking': Методmodule.link()был вызван, но не все Promise, возвращенные функцией связывания, еще не разрешены. -
'linked': Модуль успешно связан, и все его зависимости связаны, ноmodule.evaluate()еще не был вызван. -
'evaluating': Модуль оценивается через вызовmodule.evaluate()на самом себе или родительском модуле. -
'evaluated': Модуль успешно оценен. -
'errored': Модуль был оценен, но при этом было сгенерировано исключение.
За исключением 'errored', эта строка состояния соответствует полю [[Status]] записи циклического модуля из спецификации. 'errored' соответствует 'evaluated' в спецификации, но с [[EvaluationError]] установленным в значение, отличное от undefined.
Класс: vm.SourceTextModule
Эта функция доступна только при включённом флаге команды --experimental-vm-modules.
- Расширяет: <vm.Модуль>
Класс vm.SourceTextModule предоставляет запись модуля исходного текста (Source Text Module Record), как определено в спецификации ECMAScript.
new vm.SourceTextModule(code[, options])
-
code<строка> JavaScript-код модуля для парсинга -
options-
identifier<строка> Строка, используемая в трассировках стека. По умолчанию:'vm:module(i)', гдеi— контекстно-зависимый возрастающий индекс. -
cachedData<Буфер> | <Тип массива> | <DataView> Предоставляет необязательныеBufferилиTypedArray, илиDataView, с данными кэша кода V8 для предоставленного источника.codeдолжен совпадать с модулем, из которого был создан этотcachedData. -
context<Объект> Объект, контекстуализированный, возвращённый методомvm.createContext(), для компиляции и вычисления этогоModuleв. Если контекст не указан, модуль вычисляется в текущем контексте выполнения. -
lineOffset<целое число> Указывает смещение номера строки, отображаемое в трассировках стека, созданных этимModule. По умолчанию:0. -
columnOffset<целое число> Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, созданных этимModule. По умолчанию:0. -
initializeImportMeta<Функция> Вызывается во время вычисления этогоModuleдля инициализацииimport.meta.-
meta<import.meta> -
module<vm.SourceTextModule>
-
-
importModuleDynamically<Функция> Используется для указания способа загрузки модулей во время вычисления этого модуля, когда вызываетсяimport(). Этот параметр является частью экспериментального API модулей. Не рекомендуется использовать его в рабочей среде. Более подробная информация приведена в Разделе поддержки динамическогоimport()в API компиляции.
-
Создаёт новый экземпляр SourceTextModule.
Свойства объекта import.meta, которые являются объектами, могут позволить модулю получить доступ к информации за пределами указанного context. Для создания объектов в определённом контексте используйте vm.runInContext().
MJS модули
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);
CJS модули
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()
- Возвращает: <Буфер>
Создаёт кэш кода, который может быть использован с параметром cachedData конструктора SourceTextModule. Возвращает 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 }); copy Класс: vm.SyntheticModule
Эта функция доступна только при включённом флаге команды --experimental-vm-modules.
- Расширяет: <vm.Модуль>
Класс vm.SyntheticModule предоставляет запись синтетического модуля (Synthetic Module Record), как определено в спецификации WebIDL. Синтетические модули предназначены для предоставления универсального интерфейса для экспонирования источников, не являющихся 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... copy
new vm.SyntheticModule(exportNames, evaluateCallback[, options])
-
exportNames<массив строк> Массив имён, которые будут экспортированы из модуля. -
evaluateCallback<Функция> Вызывается при оценке модуля. -
options-
identifier<строка> Строка, используемая в трассировках стека. По умолчанию:'vm:module(i)', гдеi— контекстно-зависимый возрастающий индекс. -
context<Объект> Объект, контекстуализированный, возвращённый методомvm.createContext(), для компиляции и вычисления этогоModuleв.
-
Создаёт новый экземпляр SyntheticModule.
Объекты, назначенные экспорту этого экземпляра, могут позволить импортёрам модуля получить доступ к информации за пределами указанного context. Для создания объектов в определённом контексте используйте vm.runInContext().
syntheticModule.setExport(name, value)
Этот метод используется после связывания модуля для установки значений экспортов. Если он вызывается до связывания модуля, будет выброшено исключение ERR_VM_MODULE_STATUS.
MJS модули
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);
CJS модули
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<строка> Тело функции для компиляции. -
params<массив строк> Массив строк, содержащий все параметры функции. -
options<объект>-
filename<строка> Указывает имя файла, используемое в трассировках стека, созданных этим скриптом. По умолчанию:''. -
lineOffset<число> Указывает смещение номера строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию:0. -
columnOffset<число> Указывает смещение номера столбца первой строки, отображаемое в трассировках стека, созданных этим скриптом. По умолчанию:0. -
cachedData<Буфер> | <Тип массива> | <DataView> Предоставляет необязательныйBufferилиTypedArray, илиDataViewс данными кэша кода V8 для предоставленного исходного кода. Это должно быть получено из предыдущего вызоваvm.compileFunction()с такими жеcodeиparams. -
produceCachedData<логическое значение> Указывает, нужно ли создавать новые данные кэша. По умолчанию:false. -
parsingContext<объект> Контекстуализированный объект, в котором должна быть скомпилирована указанная функция. -
contextExtensions<массив объектов> Массив, содержащий набор расширений контекста (объекты, оборачивающие текущую область видимости), которые должны быть применены при компиляции. По умолчанию:[].
-
-
importModuleDynamically<Функция> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей во время оценки этой функции при вызовеimport(). Этот параметр является частью экспериментального API модулей. Мы не рекомендуем использовать его в рабочей среде. Подробная информация приведена в разделе Поддержка динамическогоimport()в API компиляции. - Возвращает: <Функция>
Компилирует предоставленный код в заданном контексте (если контекст не указан, используется текущий контекст) и возвращает его, обернутый в функцию с заданным params.
vm.constants
Возвращает объект, содержащий часто используемые константы для операций VM.
vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER
Константа, которую можно использовать в качестве параметра importModuleDynamically для vm.Script и vm.compileFunction(), чтобы Node.js использовал стандартный загрузчик ESM из главного контекста для загрузки запрошенного модуля.
Подробная информация приведена в разделе Поддержка динамического import() в API компиляции.
vm.createContext([contextObject[, options]])
-
contextObject<Объект> -
options<Объект>-
name<строка> Читаемое имя нового контекста. По умолчанию:'VM Context i', гдеi— возрастающий числовой индекс созданного контекста. -
origin<строка> Происхождение, соответствующее созданному контексту для отображения. Происхождение должно быть отформатировано как URL, но содержать только схему, хост и порт (при необходимости), подобно значению свойстваurl.originобъектаURL. Важно, что эта строка должна опускать конечный слэш, так как он обозначает путь. По умолчанию:''. -
codeGeneration<Объект>-
strings<логическое> Если установлено в false, любые вызовыevalили конструкторов функций (Function,GeneratorFunction, и т. д.) будут вызывать ошибкуEvalError. По умолчанию:true. -
wasm<логическое> Если установлено в false, любая попытка компиляции модуля WebAssembly вызовет ошибкуWebAssembly.CompileError. По умолчанию:true.
-
-
microtaskMode<строка> Если установлено вafterEvaluate, микрозадачи (задачи, запланированные с помощьюPromiseиasync function) будут выполняться сразу после того, как скрипт пройдет черезscript.runInContext(). В этом случае они включены в области видимостиtimeoutиbreakOnSigint. -
importModuleDynamically<Функция> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей при вызовеimport()в этом контексте без скрипта-ссылочника или модуля. Эта опция является частью экспериментального API модулей. Мы не рекомендуем использовать её в рабочей среде. Более подробная информация представлена в разделе Поддержка динамическогоimport()в API компиляции.
-
- Возвращает: <Объект> объект контекста.
Если задан 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 copy Если contextObject опущено (или явно передано как undefined), будет возвращён новый, пустой объект контекста.
Метод vm.createContext() главным образом полезен для создания одного контекста, который можно использовать для запуска нескольких скриптов. Например, при эмуляции веб-браузера, метод можно использовать для создания одного контекста, представляющего глобальный объект окна, а затем запуска всех тегов <script> вместе в этом контексте.
Предоставленные name и origin контекста становятся видимыми через API инспектора.
vm.isContext(object)
-
object<Объект> - Возвращает: <логическое>
Возвращает true , если данный объект object был сконтекстуализирован с помощью vm.createContext().
vm.measureMemory([options])
Измерьте память, известную V8, и используемую всеми контекстами, известными текущему изолированному V8, или основному контексту.
-
options<Объект> Необязательно.-
mode<строка> Либо'summary', либо'detailed'. В режиме сводки будет возвращена только измеренная память основного контекста. В детальном режиме будет возвращена память, измеренная для всех контекстов, известных текущему изолированному V8. По умолчанию:'summary' -
execution<строка> Либо'default', либо'eager'. При стандартном выполнении промис не разрешится, пока не начнется следующий запланированный сбор мусора, что может занять некоторое время (или никогда, если программа завершится до следующего сбора мусора). При немедленном выполнении сборка мусора будет запущена немедленно для измерения памяти. По умолчанию:'default'
-
- Возвращает: <Промис> Если память была успешно измерена, промис разрешится объектом, содержащим информацию об использовании памяти. В противном случае он будет отклонен с ошибкой
ERR_CONTEXT_NOT_INITIALIZED.
Формат объекта, с которым возвращаемый промис может разрешиться, специфичен для движка 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);
}); copy
vm.runInContext(code, contextifiedObject[, options])
-
code<строка> JavaScript-код для компиляции и выполнения. -
contextifiedObject<Объект> контекстуализированный объект, который будет использоваться какglobalпри компиляции и выполненииcode. -
options<Объект> | <строка>-
filename<строка> Указывает имя файла, используемое в отладке стека, создаваемой этим скриптом. По умолчанию:'evalmachine.<anonymous>'. -
lineOffset<число> Указывает смещение номера строки, отображаемое в отладке стека, создаваемой этим скриптом. По умолчанию:0. -
columnOffset<число> Указывает смещение номера столбца первой строки, отображаемое в отладке стека, создаваемой этим скриптом. По умолчанию:0. -
displayErrors<булево> Еслиtrue, при возникновенииErrorво время компиляцииcode, строка кода, вызвавшая ошибку, добавляется к отладке стека. По умолчанию:true. -
timeout<целое> Указывает количество миллисекунд для выполненияcodeперед завершением выполнения. Если выполнение завершается, будет выброшено исключениеError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<булево> Еслиtrue, получениеSIGINT( Ctrl + C ) завершит выполнение и выбросит исключениеError. Существующие обработчики события, прикрепленные с помощьюprocess.on('SIGINT'), отключаются во время выполнения скрипта, но продолжают работать после него. По умолчанию:false. -
cachedData<Буфер> | <Массив типов> | <DataView> Предоставляет необязательныйBufferилиTypedArray, илиDataViewс данными кэша кода V8 для предоставленного исходного кода. -
importModuleDynamically<Функция> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей во время выполнения этого скрипта при вызовеimport(). Эта опция является частью экспериментального API модулей. Мы не рекомендуем использовать её в рабочей среде. Для получения подробной информации см. Поддержка динамическогоimport()в API компиляции.
-
Метод 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 } copy
vm.runInNewContext(code[, contextObject[, options]])
-
code<строка> JavaScript-код для компиляции и выполнения. -
contextObject<Объект> Объект, который будет контекстуализирован. Еслиundefined, будет создан новый объект. -
options<Объект> | <строка>-
filename<строка> Указывает имя файла, используемое в отладке стека, созданной этим скриптом. По умолчанию:'evalmachine.<anonymous>'. -
lineOffset<число> Указывает смещение номера строки, отображаемое в отладке стека, созданной этим скриптом. По умолчанию:0. -
columnOffset<число> Указывает смещение номера столбца первой строки, отображаемое в отладке стека, созданной этим скриптом. По умолчанию:0. -
displayErrors<логическое_значение> Когдаtrue, если при компиляцииcodeпроизойдётError, строка кода, вызвавшая ошибку, будет добавлена в трассировку стека. По умолчанию:true. -
timeout<целое_число> Указывает количество миллисекунд для выполненияcodeперед завершением выполнения. Если выполнение завершится, будет выброшенаError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<логическое_значение> Еслиtrue, получениеSIGINT(Ctrl+C) завершит выполнение и выброситError. Существующие обработчики события, присоединённые черезprocess.on('SIGINT'), будут отключены во время выполнения скрипта, но продолжат работать после него. По умолчанию:false. -
contextName<строка> Читабельное имя вновь созданного контекста. По умолчанию:'VM Context i', гдеi- возрастающий числовой индекс созданного контекста. -
contextOrigin<строка> Происхождение, соответствующее вновь созданному контексту для отображения. Происхождение должно быть отформатировано как URL, но содержать только схему, хост и порт (при необходимости), подобно значению свойстваurl.originобъектаURL. Важно, что эта строка должна опускать конечный слэш, так как он обозначает путь. По умолчанию:''. -
contextCodeGeneration<Объект>-
strings<логическое_значение> Если установлено в false, любые вызовыevalили конструкторов функций (Function,GeneratorFunction, и т.д.) выбросятEvalError. По умолчанию:true. -
wasm<логическое_значение> Если установлено в false, любая попытка скомпилировать модуль WebAssembly вызоветWebAssembly.CompileError. По умолчанию:true.
-
-
cachedData<Буфер> | <Тип_массива> | <DataView> Предоставляет необязательныйBufferилиTypedArray, илиDataViewс данными кэша кода V8 для предоставленного источника. -
importModuleDynamically<Функция> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания, как должны загружаться модули во время оценки этого скрипта при вызовеimport(). Этот параметр относится к экспериментальному API модулей. Мы не рекомендуем использовать его в рабочей среде. Для получения подробной информации см. Поддержка динамическогоimport()в API компиляции. -
microtaskMode<строка> Если установлено вafterEvaluate, микрозадачи (задачи, запланированные черезPromiseиasync functions) будут выполняться немедленно после выполнения скрипта. В этом случае они включены в областиtimeoutиbreakOnSigint.
-
- Возвращает: <любой> результат последнего оператора, выполненного в скрипте.
vm.runInNewContext() сначала контекстуализирует переданный contextObject (или создаёт новый contextObject, если передан как undefined), компилирует 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' } copy
vm.runInThisContext(code[, options])
-
code<строка> JavaScript-код для компиляции и выполнения. -
options<Объект> | <строка>-
filename<строка> Указывает имя файла, используемое в отладке стека, созданной этим скриптом. По умолчанию:'evalmachine.<anonymous>'. -
lineOffset<число> Указывает смещение номера строки, отображаемое в отладке стека, созданной этим скриптом. По умолчанию:0. -
columnOffset<число> Указывает смещение номера столбца первой строки, отображаемое в отладке стека, созданной этим скриптом. По умолчанию:0. -
displayErrors<логическое значение> Приtrue, если при компиляцииcodeвозникаетError, строка кода, вызвавшая ошибку, добавляется к отладке стека. По умолчанию:true. -
timeout<целое число> Указывает количество миллисекунд для выполненияcodeперед завершением выполнения. Если выполнение завершается, будет выброшена ошибкаError. Это значение должно быть строго положительным целым числом. -
breakOnSigint<логическое значение> Еслиtrue, получениеSIGINT(Ctrl+C) завершит выполнение и вызоветError. Существующие обработчики события, подключенные черезprocess.on('SIGINT'), отключаются во время выполнения скрипта, но продолжают работать после него. По умолчанию:false. -
cachedData<Буфер> | <Массив_типов> | <DataView> Предоставляет необязательныйBufferилиTypedArray, илиDataViewс данными кэша кода V8 для предоставленного исходного кода. -
importModuleDynamically<Функция> | <vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER> Используется для указания способа загрузки модулей во время оценки этого скрипта при вызовеimport(). Эта опция является частью экспериментального API модулей. Мы не рекомендуем использовать ее в производственной среде. Для получения подробной информации см. Поддержка динамическогоimport()в API компиляции.
-
- Возвращает: <любой> результат последнего выполненного оператора в скрипте.
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' copy Поскольку vm.runInThisContext() не имеет доступа к локальному объему, localVar остается неизменным. В отличие от eval(), имеет доступ к локальному объему, поэтому значение localVar изменяется. Таким образом, vm.runInThisContext() очень похож на непрямой eval() вызов, например (0,eval)('code').
Пример: Запуск HTTP-сервера внутри виртуальной машины
При использовании script.runInThisContext() или vm.runInThisContext(), код выполняется в текущем глобальном контексте V8. Переданный этому контексту виртуальной машины код будет иметь свой изолированный объем.
Для запуска простого веб-сервера с помощью модуля node:http переданный контексту код должен либо вызвать 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); copy require() в данном случае разделяет состояние с контекстом, из которого он передан. Это может создавать риски при выполнении кода, которому нельзя доверять, например, при нежелательном изменении объектов в контексте.
Что означает "контекстизация" объекта?
Весь JavaScript, выполняемый в Node.js, выполняется в области действия "контекста". Согласно Руководству разработчика V8 Embedder:
В V8, контекст — это среда выполнения, позволяющая запускать отдельные, не связанные друг с другом, JavaScript-приложения в одной инстанции V8. Вы должны явно указать контекст, в котором вы хотите выполнить любой JavaScript-код.
При вызове метода vm.createContext() , аргумент contextObject (или новый созданный объект, если contextObject равен undefined ) внутренне связывается с новой инстанцией контекста V8. Этот контекст V8 предоставляет code для использования методами модуля node:vm с изолированной глобальной средой, в которой он может работать. Процесс создания контекста V8 и его связывания с contextObject и есть то, что в этом документе называется "контекстизацией" объекта.
Взаимодействие таймаутов с асинхронными задачами и промисами
Promise и async function могут планировать задачи, выполняемые JavaScript-движком асинхронно. По умолчанию эти задачи выполняются после того, как все JavaScript-функции в текущем стеке завершат выполнение. Это позволяет избежать влияния опций timeout и breakOnSigint.
Например, следующий код, выполненный vm.runInNewContext() с таймаутом в 5 миллисекунд, планирует бесконечный цикл для выполнения после разрешения промиса. Планируемый цикл никогда не прерывается таймаутом:
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'); copy Это можно исправить, передав microtaskMode: 'afterEvaluate' коду, создающему Context:
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' },
); copy В этом случае, микрозадача, запланированная через promise.then(), будет выполнена до возвращения из vm.runInNewContext(), и будет прервана функциональностью timeout. Это относится только к коду, выполняемому в контексте vm.Context, поэтому, например, vm.runInThisContext() не использует эту опцию.
Обработчики промисов попадают в очередь микрозадач контекста, в котором они были созданы. Например, если () => loop() заменить на просто loop в приведенном выше примере, loop будет помещено в глобальную очередь микрозадач, потому что это функция из внешнего (главного) контекста, и поэтому также сможет избежать таймаута.
Если асинхронные функции планирования, такие как process.nextTick(), queueMicrotask(), setTimeout(), setImmediate(), и т.д., доступны внутри vm.Context, функции, переданные им, будут добавлены в общие очереди, которые используются всеми контекстами. Следовательно, коллбеки, переданные этим функциям, также не контролируются таймаутом.
Поддержка динамического импорта в API компиляции
Следующие API поддерживают параметр importModuleDynamically, чтобы включить динамический импорт в коде, скомпилированном модулем vm.
new vm.Scriptvm.compileFunction()new vm.SourceTextModulevm.runInThisContext()vm.runInContext()vm.runInNewContext()vm.createContext()
Этот параметр всё ещё является частью экспериментального API модулей. Не рекомендуется использовать его в рабочей среде.
Если параметр importModuleDynamically не указан или не определён
Если этот параметр не указан или имеет значение 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 в новом контексте.
Модули CJS
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);
Модули MJS
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); Этот параметр также позволяет скрипту или функции загружать пользовательские модули:
Модули MJS
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);
Модули CJS
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, кэш не будет использован, если у него есть разные параметры поиска. Дляfilenameкоторые не являются строками URL, в настоящее время нет способа обойти поведение кэширования.
Если importModuleDynamically является функцией
Если importModuleDynamically является функцией, она будет вызвана при вызове import() в скомпилированном коде, чтобы пользователи могли настроить способ компиляции и оценки запрошенного модуля. В настоящее время экземпляр Node.js должен быть запущен с флагом --experimental-vm-modules для работы этого параметра. Если флаг не установлен, этот коллбэк будет проигнорирован. Если оценённый код фактически вызывает import(), результат будет отклонен с ошибкой ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING_FLAG.
У коллбэка importModuleDynamically(specifier, referrer, importAttributes) следующий сигнатура:
-
specifier<строка> спецификатор, переданныйimport() -
referrer<vm.Script> | <Функция> | <vm.SourceTextModule> | <Объект> Ссылка — это скомпилированныйvm.Scriptдляnew vm.Script,vm.runInThisContext,vm.runInContextиvm.runInNewContext. Это скомпилированныйFunctionдляvm.compileFunction, скомпилированныйvm.SourceTextModuleдляnew vm.SourceTextModuleи контекстObjectдляvm.createContext(). -
importAttributes<Объект> Значение"with", переданное необязательному параметруoptionsExpression, или пустой объект, если значение не было предоставлено. - Возвращает: <Объект пространства имён модуля> | <vm.Module> Рекомендуется возвращать
vm.Moduleдля использования отслеживания ошибок и предотвращения проблем с пространствами имён, содержащимиthenэкспорт функций.
Модули MJS
// 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' } }
Модули CJS
// 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/dist/latest-v20.x/docs/api/vm.html