Skip to content

Commit 819e287

Browse files
Use workerd node:console when available (#10606)
* Use workerd `node:console` when available * Apply suggestion from @vicb * fixup! stdErr, stdOut -> stderr, stdout * fixup! bump workerd peer dep --------- Co-authored-by: Victor Berchet <[email protected]>
1 parent f550b62 commit 819e287

File tree

6 files changed

+146
-63
lines changed

6 files changed

+146
-63
lines changed

‎.changeset/calm-carrots-tie.md‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@cloudflare/unenv-preset": patch
3+
---
4+
5+
Use workerd `node:console` when it is available
6+
7+
It is enabled when the `enable_nodejs_console_module` compatibility flag is set.
8+
This flag defaults to true when `nodejs_compat` is turned on and the date is >= 2025-09-21.

‎packages/unenv-preset/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
},
5050
"peerDependencies": {
5151
"unenv": "2.0.0-rc.24",
52-
"workerd": "^1.20251125.0"
52+
"workerd": "^1.20251202.0"
5353
},
5454
"peerDependenciesMeta": {
5555
"workerd": {

‎packages/unenv-preset/src/preset.ts‎

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const nativeModules = [
4747
];
4848

4949
// Modules implemented via a mix of workerd APIs and polyfills.
50-
const hybridModules = ["console", "process"];
50+
const hybridModules = ["process"];
5151

5252
/**
5353
* Creates the Cloudflare preset for the given compatibility date and compatibility flags
@@ -77,6 +77,7 @@ export function getCloudflarePreset({
7777
const traceEventsOverrides = getTraceEventsOverrides(compat);
7878
const domainOverrides = getDomainOverrides(compat);
7979
const wasiOverrides = getWasiOverrides(compat);
80+
const consoleOverrides = getConsoleOverrides(compat);
8081

8182
// "dynamic" as they depend on the compatibility date and flags
8283
const dynamicNativeModules = [
@@ -90,6 +91,7 @@ export function getCloudflarePreset({
9091
...traceEventsOverrides.nativeModules,
9192
...domainOverrides.nativeModules,
9293
...wasiOverrides.nativeModules,
94+
...consoleOverrides.nativeModules,
9395
];
9496

9597
// "dynamic" as they depend on the compatibility date and flags
@@ -104,6 +106,7 @@ export function getCloudflarePreset({
104106
...traceEventsOverrides.hybridModules,
105107
...domainOverrides.hybridModules,
106108
...wasiOverrides.hybridModules,
109+
...consoleOverrides.hybridModules,
107110
];
108111

109112
return {
@@ -136,7 +139,7 @@ export function getCloudflarePreset({
136139
global: false,
137140
clearImmediate: false,
138141
setImmediate: false,
139-
console: "@cloudflare/unenv-preset/node/console",
142+
...consoleOverrides.inject,
140143
process: "@cloudflare/unenv-preset/node/process",
141144
},
142145
polyfill: ["@cloudflare/unenv-preset/polyfill/performance"],
@@ -510,3 +513,47 @@ function getWasiOverrides({
510513
hybridModules: [],
511514
};
512515
}
516+
517+
/**
518+
* Returns the overrides for `node:console` (unenv or workerd)
519+
*
520+
* The native console implementation:
521+
* - is enabled starting from 2025-09-21
522+
* - can be enabled with the "enable_nodejs_console_module" flag
523+
* - can be disabled with the "disable_nodejs_console_module" flag
524+
*/
525+
function getConsoleOverrides({
526+
compatibilityDate,
527+
compatibilityFlags,
528+
}: {
529+
compatibilityDate: string;
530+
compatibilityFlags: string[];
531+
}): {
532+
nativeModules: string[];
533+
hybridModules: string[];
534+
inject: Record<string, string>;
535+
} {
536+
const disabledByFlag = compatibilityFlags.includes(
537+
"disable_nodejs_console_module"
538+
);
539+
540+
const enabledByFlag = compatibilityFlags.includes(
541+
"enable_nodejs_console_module"
542+
);
543+
const enabledByDate = compatibilityDate >= "2025-09-21";
544+
545+
const enabled = (enabledByFlag || enabledByDate) && !disabledByFlag;
546+
547+
// The native `console` module implements all the node APIs so we can use them directly
548+
return enabled
549+
? {
550+
nativeModules: ["console"],
551+
hybridModules: [],
552+
inject: {},
553+
}
554+
: {
555+
nativeModules: [],
556+
hybridModules: ["console"],
557+
inject: { console: "@cloudflare/unenv-preset/node/console" },
558+
};
559+
}

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,39 @@ const localTestConfigs: TestConfig[] = [
201201
},
202202
},
203203
],
204+
// node:console
205+
[
206+
{
207+
name: "console disabled by date",
208+
compatibilityDate: "2024-09-23",
209+
expectRuntimeFlags: {
210+
enable_nodejs_console_module: false,
211+
},
212+
},
213+
{
214+
name: "console enabled by date",
215+
compatibilityDate: "2025-09-21",
216+
expectRuntimeFlags: {
217+
enable_nodejs_console_module: true,
218+
},
219+
},
220+
{
221+
name: "console enabled by flag",
222+
compatibilityDate: "2024-09-23",
223+
compatibilityFlags: ["enable_nodejs_console_module"],
224+
expectRuntimeFlags: {
225+
enable_nodejs_console_module: true,
226+
},
227+
},
228+
{
229+
name: "console disabled by flag",
230+
compatibilityDate: "2025-09-21",
231+
compatibilityFlags: ["disable_nodejs_console_module"],
232+
expectRuntimeFlags: {
233+
enable_nodejs_console_module: false,
234+
},
235+
},
236+
],
204237
// node:process v2
205238
[
206239
{

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

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,59 @@ function generateTestListResponse(testName: string): Response {
5959
// Test functions executed on worked.
6060
// The test can be executing by fetching the `/${testName}` url.
6161
export const WorkerdTests: Record<string, () => void> = {
62+
async testConsole() {
63+
const importNamespace = await import("node:console");
64+
const globalObject = globalThis.console;
65+
66+
assert.strictEqual(
67+
globalThis.console,
68+
importNamespace.default,
69+
"expected `console` to be the same as `consoleImport.default`"
70+
);
71+
72+
assertTypeOf(importNamespace, "default", "object");
73+
74+
for (const target of [importNamespace, globalObject]) {
75+
assertTypeOfProperties(target, {
76+
Console: "function",
77+
assert: "function",
78+
clear: "function",
79+
count: "function",
80+
countReset: "function",
81+
debug: "function",
82+
dir: "function",
83+
dirxml: "function",
84+
error: "function",
85+
group: "function",
86+
groupCollapsed: "function",
87+
groupEnd: "function",
88+
info: "function",
89+
log: "function",
90+
profile: "function",
91+
profileEnd: "function",
92+
table: "function",
93+
time: "function",
94+
timeEnd: "function",
95+
timeLog: "function",
96+
trace: "function",
97+
warn: "function",
98+
// These undocumented APIs are supported in Node.js, unenv, and workerd natively.
99+
context: "function",
100+
createTask: "function",
101+
});
102+
}
103+
104+
// These undocumented APIs are only on the global object not the import.
105+
assertTypeOfProperties(global.console, {
106+
_stderr: "object",
107+
_stdout: "object",
108+
_times: "object",
109+
_stdoutErrorHandler: "function",
110+
_stderrErrorHandler: "function",
111+
_ignoreErrors: "boolean",
112+
});
113+
},
114+
62115
async testCryptoGetRandomValues() {
63116
const crypto = await import("node:crypto");
64117

‎pnpm-lock.yaml‎

Lines changed: 2 additions & 60 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)