Skip to content

Commit 9a565d5

Browse files
[unenv-preset] Support native child_process module when experimental flag is enabled (#12573)
1 parent caf9b11 commit 9a565d5

File tree

4 files changed

+112
-0
lines changed

4 files changed

+112
-0
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@cloudflare/unenv-preset": minor
3+
---
4+
5+
Add support for native `node:child_process` module from workerd when the `enable_nodejs_child_process_module` compatibility flag is enabled.
6+
7+
This feature is currently experimental and requires both the `enable_nodejs_child_process_module` and `experimental` compatibility flags to be set.

packages/unenv-preset/src/preset.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export function getCloudflarePreset({
8282
const processOverrides = getProcessOverrides(compat);
8383
const v8Overrides = getV8Overrides(compat);
8484
const ttyOverrides = getTtyOverrides(compat);
85+
const childProcessOverrides = getChildProcessOverrides(compat);
8586

8687
// "dynamic" as they depend on the compatibility date and flags
8788
const dynamicNativeModules = [
@@ -105,6 +106,7 @@ export function getCloudflarePreset({
105106
...processOverrides.nativeModules,
106107
...v8Overrides.nativeModules,
107108
...ttyOverrides.nativeModules,
109+
...childProcessOverrides.nativeModules,
108110
];
109111

110112
// "dynamic" as they depend on the compatibility date and flags
@@ -128,6 +130,7 @@ export function getCloudflarePreset({
128130
...processOverrides.hybridModules,
129131
...v8Overrides.hybridModules,
130132
...ttyOverrides.hybridModules,
133+
...childProcessOverrides.hybridModules,
131134
];
132135

133136
return {
@@ -978,3 +981,39 @@ function getTtyOverrides({
978981
hybridModules: [],
979982
};
980983
}
984+
985+
/**
986+
* Returns the overrides for `node:child_process` (unenv or workerd)
987+
*
988+
* The native child_process implementation:
989+
* - is experimental and has no default enable date
990+
* - can be enabled with the "enable_nodejs_child_process_module" flag
991+
* - can be disabled with the "disable_nodejs_child_process_module" flag
992+
*/
993+
function getChildProcessOverrides({
994+
compatibilityFlags,
995+
}: {
996+
compatibilityDate: string;
997+
compatibilityFlags: string[];
998+
}): { nativeModules: string[]; hybridModules: string[] } {
999+
const disabledByFlag = compatibilityFlags.includes(
1000+
"disable_nodejs_child_process_module"
1001+
);
1002+
1003+
const enabledByFlag =
1004+
compatibilityFlags.includes("enable_nodejs_child_process_module") &&
1005+
compatibilityFlags.includes("experimental");
1006+
1007+
const enabled = enabledByFlag && !disabledByFlag;
1008+
1009+
// When enabled, use the native `child_process` module from workerd
1010+
return enabled
1011+
? {
1012+
nativeModules: ["child_process"],
1013+
hybridModules: [],
1014+
}
1015+
: {
1016+
nativeModules: [],
1017+
hybridModules: [],
1018+
};
1019+
}

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,33 @@ const localTestConfigs: TestConfig[] = [
676676
},
677677
},
678678
],
679+
// node:child_process (experimental, no default enable date)
680+
[
681+
// TODO: add test for disabled by date (no date defined yet)
682+
// TODO: add test for enabled by date (no date defined yet)
683+
{
684+
name: "child_process enabled by flag",
685+
compatibilityDate: "2024-09-23",
686+
compatibilityFlags: [
687+
"enable_nodejs_child_process_module",
688+
"experimental",
689+
],
690+
expectRuntimeFlags: {
691+
enable_nodejs_child_process_module: true,
692+
},
693+
},
694+
{
695+
name: "child_process disabled by flag",
696+
compatibilityDate: "2024-09-23",
697+
compatibilityFlags: [
698+
"disable_nodejs_child_process_module",
699+
"experimental",
700+
],
701+
expectRuntimeFlags: {
702+
enable_nodejs_child_process_module: false,
703+
},
704+
},
705+
],
679706
].flat() as TestConfig[];
680707

681708
describe.each(localTestConfigs)(

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,45 @@ export const WorkerdTests: Record<string, () => void> = {
913913
assert.doesNotThrow(() => new tty.ReadStream(0));
914914
assert.doesNotThrow(() => new tty.WriteStream(1));
915915
},
916+
917+
async testChildProcess() {
918+
const childProcess = await import("node:child_process");
919+
920+
// Common exports (both unenv stub and native workerd)
921+
assertTypeOfProperties(childProcess, {
922+
ChildProcess: "function",
923+
exec: "function",
924+
execFile: "function",
925+
execFileSync: "function",
926+
execSync: "function",
927+
fork: "function",
928+
spawn: "function",
929+
spawnSync: "function",
930+
});
931+
932+
assertTypeOfProperties(childProcess.default, {
933+
ChildProcess: "function",
934+
exec: "function",
935+
execFile: "function",
936+
execFileSync: "function",
937+
execSync: "function",
938+
fork: "function",
939+
spawn: "function",
940+
spawnSync: "function",
941+
});
942+
943+
// Both implementations throw when calling spawn()
944+
assert.throws(
945+
() => childProcess.spawn("ls"),
946+
/not implemented|ERR_METHOD_NOT_IMPLEMENTED/
947+
);
948+
949+
// Both implementations throw when calling exec()
950+
assert.throws(
951+
() => childProcess.exec("ls"),
952+
/not implemented|ERR_METHOD_NOT_IMPLEMENTED/
953+
);
954+
},
916955
};
917956

918957
/**

0 commit comments

Comments
 (0)