Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cold-hands-reply.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cloudflare/unenv-preset": minor
---

add support for native `node:os`
2 changes: 1 addition & 1 deletion packages/unenv-preset/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
},
"peerDependencies": {
"unenv": "2.0.0-rc.19",
"workerd": "^1.20250730.0"
"workerd": "^1.20250802.0"
},
"peerDependenciesMeta": {
"workerd": {
Expand Down
61 changes: 54 additions & 7 deletions packages/unenv-preset/src/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,23 @@ export function getCloudflarePreset({
compatibilityFlags,
});

const osOverrides = getOsOverrides({
compatibilityDate,
compatibilityFlags,
});

// "dynamic" as they depend on the compatibility date and flags
const dynamicNativeModules = [
...nativeModules,
...httpOverrides.nativeModules,
...osOverrides.nativeModules,
];

// "dynamic" as they depend on the compatibility date and flags
const dynamicHybridModules = [
...hybridModules,
...httpOverrides.hybridModules,
...osOverrides.hybridModules,
];

return {
Expand Down Expand Up @@ -138,6 +145,7 @@ export function getCloudflarePreset({
*
* The native http server APIS implementation:
* - can be enabled with the "enable_nodejs_http_server_modules" flag
* - can be disabled with the "disable_nodejs_http_server_modules" flag
*/
function getHttpOverrides({
compatibilityDate,
Expand All @@ -149,31 +157,31 @@ function getHttpOverrides({
const httpDisabledByFlag = compatibilityFlags.includes(
"disable_nodejs_http_modules"
);
const httpEnabledByFlags = compatibilityFlags.includes(
const httpEnabledByFlag = compatibilityFlags.includes(
"enable_nodejs_http_modules"
);
const httpEnabledByDate = compatibilityDate >= "2025-08-15";

const httpEnabled =
(httpEnabledByFlags || httpEnabledByDate) && !httpDisabledByFlag;
(httpEnabledByFlag || httpEnabledByDate) && !httpDisabledByFlag;

if (!httpEnabled) {
// use the unenv polyfill
return { nativeModules: [], hybridModules: [] };
}

const httpServerEnabledByFlags = compatibilityFlags.includes(
"enable_nodejs_http_server_modules"
);
const httpServerEnabledByFlag =
compatibilityFlags.includes("enable_nodejs_http_server_modules") &&
compatibilityFlags.includes("experimental");

const httpServerDisabledByFlags = compatibilityFlags.includes(
const httpServerDisabledByFlag = compatibilityFlags.includes(
"disable_nodejs_http_server_modules"
);

// Note that `httpServerEnabled` requires `httpEnabled`
// TODO: add `httpServerEnabledByDate` when a default date is set
const httpServerEnabled =
httpServerEnabledByFlags && !httpServerDisabledByFlags;
httpServerEnabledByFlag && !httpServerDisabledByFlag;

// Override unenv base aliases with native and hybrid modules
// `node:https` is fully implemented by workerd if both flags are enabled
Expand All @@ -189,3 +197,42 @@ function getHttpOverrides({
hybridModules: httpServerEnabled ? ["http"] : ["http", "https"],
};
}

/**
* Returns the overrides for `node:os` (unenv or workerd)
*
* The native http implementation:
* - can be enabled with the "enable_nodejs_os_module" flag
* - can be disabled with the "disable_nodejs_os_module" flag
*/
function getOsOverrides({
// eslint-disable-next-line unused-imports/no-unused-vars
compatibilityDate,
compatibilityFlags,
}: {
compatibilityDate: string;
compatibilityFlags: string[];
}): { nativeModules: string[]; hybridModules: string[] } {
const disabledByFlag = compatibilityFlags.includes(
"disable_nodejs_os_module"
);

const enabledByFlag =
compatibilityFlags.includes("enable_nodejs_os_module") &&
compatibilityFlags.includes("experimental");

// TODO: add `enabledByDate` when a default date is set
const enabled = enabledByFlag && !disabledByFlag;

// The native os module implements all the APIs.
// It can then be used as a native module.
return enabled
? {
nativeModules: ["os"],
hybridModules: [],
}
: {
nativeModules: [],
hybridModules: [],
};
}
34 changes: 32 additions & 2 deletions packages/wrangler/e2e/unenv-preset/preset.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ type TestConfig = {
compatibilityFlags?: string[];
// Assert runtime compatibility flag values
expectRuntimeFlags?: {
// Whether the http modules are enabled
enable_nodejs_http_modules: boolean;
enable_nodejs_http_modules?: boolean;
enable_nodejs_http_server_modules?: boolean;
enable_nodejs_os_module?: boolean;
};
};

Expand Down Expand Up @@ -95,6 +96,35 @@ const testConfigs: TestConfig[] = [
},
},
],
// node:os
[
{
name: "os disabled by date",
compatibilityDate: "2025-07-26",
compatibilityFlags: ["experimental"],
expectRuntimeFlags: {
enable_nodejs_os_module: false,
},
},
// TODO: add a config when os is enabled by default (date no set yet)
{
name: "os enabled by flag",
compatibilityDate: "2025-07-26",
compatibilityFlags: ["enable_nodejs_os_module", "experimental"],
expectRuntimeFlags: {
enable_nodejs_os_module: true,
},
},
// TODO: change the date pass the default enabled date (date not set yet)
{
name: "os disabled by flag",
compatibilityDate: "2025-07-26",
compatibilityFlags: ["disable_nodejs_os_module", "experimental"],
expectRuntimeFlags: {
enable_nodejs_os_module: false,
},
},
],
].flat();

describe.each(testConfigs)(
Expand Down
16 changes: 12 additions & 4 deletions packages/wrangler/e2e/unenv-preset/worker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ export const WorkerdTests: Record<string, () => void> = {
},

async testHttp() {
const http = await import("http");
const http = await import("node:http");

const useNativeHttp = getRuntimeFlagValue("enable_nodejs_http_modules");

Expand All @@ -270,7 +270,7 @@ export const WorkerdTests: Record<string, () => void> = {
},

async testHttps() {
const https = await import("https");
const https = await import("node:https");

assert.strictEqual(typeof https.Agent, "function");
assert.strictEqual(typeof https.get, "function");
Expand All @@ -279,7 +279,7 @@ export const WorkerdTests: Record<string, () => void> = {
},

async testHttpServer() {
const http = await import("http");
const http = await import("node:http");

const useNativeHttp = getRuntimeFlagValue(
"enable_nodejs_http_server_modules"
Expand All @@ -303,7 +303,7 @@ export const WorkerdTests: Record<string, () => void> = {
},

async testHttpsServer() {
const https = await import("https");
const https = await import("node:https");

const useNativeHttp = getRuntimeFlagValue(
"enable_nodejs_http_server_modules"
Expand All @@ -325,4 +325,12 @@ export const WorkerdTests: Record<string, () => void> = {
assert.throws(() => new https.Server(), /not implemented/);
}
},

async testOs() {
const os = await import("node:os");

assert.strictEqual(typeof os.arch(), "string");
assert.strictEqual(typeof os.freemem(), "number");
assert.strictEqual(typeof os.availableParallelism(), "number");
},
};
62 changes: 2 additions & 60 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading