Skip to content

Commit 165d6ba

Browse files
Use native node:vm modue when available
1 parent 9fc10d3 commit 165d6ba

File tree

4 files changed

+132
-0
lines changed

4 files changed

+132
-0
lines changed

.changeset/upset-ducks-sin.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@cloudflare/unenv-preset": patch
3+
---
4+
5+
Use native node:vm module when available

packages/unenv-preset/src/preset.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export function getCloudflarePreset({
7272
const http2Overrides = getHttp2Overrides(compat);
7373
const osOverrides = getOsOverrides(compat);
7474
const fsOverrides = getFsOverrides(compat);
75+
const vmOverrides = getVmOverrides(compat);
7576

7677
// "dynamic" as they depend on the compatibility date and flags
7778
const dynamicNativeModules = [
@@ -80,6 +81,7 @@ export function getCloudflarePreset({
8081
...http2Overrides.nativeModules,
8182
...osOverrides.nativeModules,
8283
...fsOverrides.nativeModules,
84+
...vmOverrides.nativeModules,
8385
];
8486

8587
// "dynamic" as they depend on the compatibility date and flags
@@ -89,6 +91,7 @@ export function getCloudflarePreset({
8991
...http2Overrides.hybridModules,
9092
...osOverrides.hybridModules,
9193
...fsOverrides.hybridModules,
94+
...vmOverrides.hybridModules,
9295
];
9396

9497
return {
@@ -305,3 +308,39 @@ function getFsOverrides({
305308
hybridModules: [],
306309
};
307310
}
311+
312+
/**
313+
* Returns the overrides for `node:vm` (unenv or workerd)
314+
*
315+
* The native vm implementation:
316+
* - is enabled starting from 2025-10-01
317+
* - can be enabled with the "enable_nodejs_vm_module" flag
318+
* - can be disabled with the "disable_nodejs_vm_module" flag
319+
*/
320+
function getVmOverrides({
321+
compatibilityDate,
322+
compatibilityFlags,
323+
}: {
324+
compatibilityDate: string;
325+
compatibilityFlags: string[];
326+
}): { nativeModules: string[]; hybridModules: string[] } {
327+
const disabledByFlag = compatibilityFlags.includes(
328+
"disable_nodejs_vm_module"
329+
);
330+
331+
const enabledByFlag = compatibilityFlags.includes("enable_nodejs_vm_module");
332+
const enabledByDate = compatibilityDate >= "2025-10-01";
333+
334+
const enabled = (enabledByFlag || enabledByDate) && !disabledByFlag;
335+
336+
// The native `vm` module implements all the node APIs so we can use it directly
337+
return enabled
338+
? {
339+
nativeModules: ["vm"],
340+
hybridModules: [],
341+
}
342+
: {
343+
nativeModules: [],
344+
hybridModules: [],
345+
};
346+
}

packages/wrangler/e2e/unenv-preset/preset.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,34 @@ const testConfigs: TestConfig[] = [
188188
},
189189
},
190190
],
191+
// node:vm
192+
[
193+
{
194+
name: "vm disabled by date",
195+
compatibilityDate: "2024-09-23",
196+
expectRuntimeFlags: {
197+
enable_nodejs_vm_module: false,
198+
},
199+
},
200+
// TODO: add a config when vm is enabled by default (>= 2025-10-01)
201+
{
202+
name: "vm enabled by flag",
203+
compatibilityDate: "2024-09-23",
204+
compatibilityFlags: ["enable_nodejs_vm_module"],
205+
expectRuntimeFlags: {
206+
enable_nodejs_vm_module: true,
207+
},
208+
},
209+
{
210+
name: "vm disabled by flag",
211+
// TODO: change the date passed the default enabled date (>= 2025-10-01)
212+
compatibilityDate: "2025-07-26",
213+
compatibilityFlags: ["disable_nodejs_vm_module"],
214+
expectRuntimeFlags: {
215+
enable_nodejs_vm_module: false,
216+
},
217+
},
218+
],
191219
].flat() as TestConfig[];
192220

193221
describe.each(testConfigs)(

packages/wrangler/e2e/unenv-preset/worker/index.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,4 +505,64 @@ export const WorkerdTests: Record<string, () => void> = {
505505
assert.strictEqual(typeof http2.connect, "function");
506506
assert.strictEqual(http2.constants.HTTP2_HEADER_STATUS, ":status");
507507
},
508+
509+
async testVm() {
510+
const vm = await import("node:vm");
511+
512+
assertType(vm.Script, "function", "vm.Script");
513+
assertType(vm.constants, "object", "vm.constants");
514+
assertType(vm.compileFunction, "function", "vm.compileFunction");
515+
assertType(vm.createContext, "function", "vm.createContext");
516+
// @ts-expect-error undocumented API
517+
assertType(vm.createScript, "function", "vm.createScript");
518+
assertType(vm.isContext, "function", "vm.isContext");
519+
assertType(vm.measureMemory, "function", "vm.measureMemory");
520+
assertType(vm.runInContext, "function", "vm.runInContext");
521+
assertType(vm.runInThisContext, "function", "vm.runInThisContext");
522+
assertType(vm.runInNewContext, "function", "vm.runInNewContext");
523+
524+
assertType(vm.default.Script, "function", "vm.default.Script");
525+
assertType(
526+
vm.default.compileFunction,
527+
"function",
528+
"vm.default.compileFunction"
529+
);
530+
assertType(vm.default.constants, "object", "vm.default.constants");
531+
assertType(
532+
vm.default.createContext,
533+
"function",
534+
"vm.default.createContext"
535+
);
536+
assertType(vm.default.isContext, "function", "vm.default.isContext");
537+
assertType(
538+
vm.default.measureMemory,
539+
"function",
540+
"vm.default.measureMemory"
541+
);
542+
assertType(vm.default.runInContext, "function", "vm.default.runInContext");
543+
assertType(
544+
vm.default.runInNewContext,
545+
"function",
546+
"vm.default.runInNewContext"
547+
);
548+
assertType(
549+
vm.default.runInThisContext,
550+
"function",
551+
"vm.default.runInThisContext"
552+
);
553+
assertType(
554+
// @ts-expect-error undocumented API
555+
vm.default.createScript,
556+
"function",
557+
"vm.default.createScript"
558+
);
559+
},
508560
};
561+
562+
function assertType(value: unknown, type: string, name: string) {
563+
assert.strictEqual(
564+
typeof value,
565+
type,
566+
`Expected ${name} to be of type ${type}, but got ${typeof value}`
567+
);
568+
}

0 commit comments

Comments
 (0)