Skip to content

Commit d028ffb

Browse files
[unenv-preset] Graduate experimental Node.js module flags to date-gated (2026-03-17) (#12763)
1 parent 0f10583 commit d028ffb

4 files changed

Lines changed: 202 additions & 99 deletions

File tree

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+
Graduate experimental Node.js module flags to date-gated flags
6+
7+
The following Node.js module compatibility flags are no longer experimental and are now automatically enabled for workers using `nodejs_compat` with a compatibility date of `2026-03-17` or later: `perf_hooks`, `v8`, `tty`, `child_process`, `worker_threads`, `readline`, and `repl`. Each flag can still be explicitly enabled or disabled via the corresponding `enable_`/`disable_` compatibility flags.

packages/unenv-preset/src/preset.ts

Lines changed: 57 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -685,20 +685,24 @@ function getStreamWrapOverrides({
685685
* Returns the overrides for `node:repl` (unenv or workerd)
686686
*
687687
* The native repl implementation:
688-
* - is experimental and has no default enable date
688+
* - is enabled starting from 2026-03-17
689689
* - can be enabled with the "enable_nodejs_repl_module" flag
690690
* - can be disabled with the "disable_nodejs_repl_module" flag
691691
*/
692-
function getReplOverrides({ compatibilityFlags }: Compatibility): Override {
692+
function getReplOverrides({
693+
compatibilityDate,
694+
compatibilityFlags,
695+
}: Compatibility): Override {
693696
const disabledByFlag = compatibilityFlags.includes(
694697
"disable_nodejs_repl_module"
695698
);
696699

697-
const enabledByFlag =
698-
compatibilityFlags.includes("enable_nodejs_repl_module") &&
699-
compatibilityFlags.includes("experimental");
700+
const enabledByFlag = compatibilityFlags.includes(
701+
"enable_nodejs_repl_module"
702+
);
703+
const enabledByDate = compatibilityDate >= "2026-03-17";
700704

701-
const enabled = enabledByFlag && !disabledByFlag;
705+
const enabled = (enabledByFlag || enabledByDate) && !disabledByFlag;
702706

703707
// When enabled, use the native `repl` module from workerd
704708
return enabled
@@ -810,20 +814,22 @@ function hasFetchIterableFixes({
810814
* Returns the overrides for `node:v8` (unenv or workerd)
811815
*
812816
* The native v8 implementation:
813-
* - is experimental and has no default enable date
817+
* - is enabled starting from 2026-03-17
814818
* - can be enabled with the "enable_nodejs_v8_module" flag
815819
* - can be disabled with the "disable_nodejs_v8_module" flag
816820
*/
817-
function getV8Overrides({ compatibilityFlags }: Compatibility): Override {
821+
function getV8Overrides({
822+
compatibilityDate,
823+
compatibilityFlags,
824+
}: Compatibility): Override {
818825
const disabledByFlag = compatibilityFlags.includes(
819826
"disable_nodejs_v8_module"
820827
);
821828

822-
const enabledByFlag =
823-
compatibilityFlags.includes("enable_nodejs_v8_module") &&
824-
compatibilityFlags.includes("experimental");
829+
const enabledByFlag = compatibilityFlags.includes("enable_nodejs_v8_module");
830+
const enabledByDate = compatibilityDate >= "2026-03-17";
825831

826-
const enabled = enabledByFlag && !disabledByFlag;
832+
const enabled = (enabledByFlag || enabledByDate) && !disabledByFlag;
827833

828834
// When enabled, use the native `v8` module from workerd
829835
return enabled
@@ -841,20 +847,22 @@ function getV8Overrides({ compatibilityFlags }: Compatibility): Override {
841847
* Returns the overrides for `node:tty` (unenv or workerd)
842848
*
843849
* The native tty implementation:
844-
* - is experimental and has no default enable date
850+
* - is enabled starting from 2026-03-17
845851
* - can be enabled with the "enable_nodejs_tty_module" flag
846852
* - can be disabled with the "disable_nodejs_tty_module" flag
847853
*/
848-
function getTtyOverrides({ compatibilityFlags }: Compatibility): Override {
854+
function getTtyOverrides({
855+
compatibilityDate,
856+
compatibilityFlags,
857+
}: Compatibility): Override {
849858
const disabledByFlag = compatibilityFlags.includes(
850859
"disable_nodejs_tty_module"
851860
);
852861

853-
const enabledByFlag =
854-
compatibilityFlags.includes("enable_nodejs_tty_module") &&
855-
compatibilityFlags.includes("experimental");
862+
const enabledByFlag = compatibilityFlags.includes("enable_nodejs_tty_module");
863+
const enabledByDate = compatibilityDate >= "2026-03-17";
856864

857-
const enabled = enabledByFlag && !disabledByFlag;
865+
const enabled = (enabledByFlag || enabledByDate) && !disabledByFlag;
858866

859867
// When enabled, use the native `tty` module from workerd
860868
return enabled
@@ -872,22 +880,24 @@ function getTtyOverrides({ compatibilityFlags }: Compatibility): Override {
872880
* Returns the overrides for `node:child_process` (unenv or workerd)
873881
*
874882
* The native child_process implementation:
875-
* - is experimental and has no default enable date
883+
* - is enabled starting from 2026-03-17
876884
* - can be enabled with the "enable_nodejs_child_process_module" flag
877885
* - can be disabled with the "disable_nodejs_child_process_module" flag
878886
*/
879887
function getChildProcessOverrides({
888+
compatibilityDate,
880889
compatibilityFlags,
881890
}: Compatibility): Override {
882891
const disabledByFlag = compatibilityFlags.includes(
883892
"disable_nodejs_child_process_module"
884893
);
885894

886-
const enabledByFlag =
887-
compatibilityFlags.includes("enable_nodejs_child_process_module") &&
888-
compatibilityFlags.includes("experimental");
895+
const enabledByFlag = compatibilityFlags.includes(
896+
"enable_nodejs_child_process_module"
897+
);
898+
const enabledByDate = compatibilityDate >= "2026-03-17";
889899

890-
const enabled = enabledByFlag && !disabledByFlag;
900+
const enabled = (enabledByFlag || enabledByDate) && !disabledByFlag;
891901

892902
// When enabled, use the native `child_process` module from workerd
893903
return enabled
@@ -905,23 +915,24 @@ function getChildProcessOverrides({
905915
* Returns the overrides for `node:worker_threads` (unenv or workerd)
906916
*
907917
* The native worker_threads implementation:
918+
* - is enabled starting from 2026-03-17
908919
* - can be enabled with the "enable_nodejs_worker_threads_module" flag
909920
* - can be disabled with the "disable_nodejs_worker_threads_module" flag
910-
* - is experimental (no default enable date)
911921
*/
912922
function getWorkerThreadsOverrides({
923+
compatibilityDate,
913924
compatibilityFlags,
914925
}: Compatibility): Override {
915926
const disabledByFlag = compatibilityFlags.includes(
916927
"disable_nodejs_worker_threads_module"
917928
);
918929

919-
const enabledByFlag =
920-
compatibilityFlags.includes("enable_nodejs_worker_threads_module") &&
921-
compatibilityFlags.includes("experimental");
930+
const enabledByFlag = compatibilityFlags.includes(
931+
"enable_nodejs_worker_threads_module"
932+
);
933+
const enabledByDate = compatibilityDate >= "2026-03-17";
922934

923-
// worker_threads is experimental, no default enable date
924-
const enabled = enabledByFlag && !disabledByFlag;
935+
const enabled = (enabledByFlag || enabledByDate) && !disabledByFlag;
925936

926937
// When enabled, use the native `worker_threads` module from workerd
927938
return enabled
@@ -939,20 +950,24 @@ function getWorkerThreadsOverrides({
939950
* Returns the overrides for `node:readline` and `node:readline/promises` (unenv or workerd)
940951
*
941952
* The native readline implementation:
942-
* - is experimental and has no default enable date
953+
* - is enabled starting from 2026-03-17
943954
* - can be enabled with the "enable_nodejs_readline_module" flag
944955
* - can be disabled with the "disable_nodejs_readline_module" flag
945956
*/
946-
function getReadlineOverrides({ compatibilityFlags }: Compatibility): Override {
957+
function getReadlineOverrides({
958+
compatibilityDate,
959+
compatibilityFlags,
960+
}: Compatibility): Override {
947961
const disabledByFlag = compatibilityFlags.includes(
948962
"disable_nodejs_readline_module"
949963
);
950964

951-
const enabledByFlag =
952-
compatibilityFlags.includes("enable_nodejs_readline_module") &&
953-
compatibilityFlags.includes("experimental");
965+
const enabledByFlag = compatibilityFlags.includes(
966+
"enable_nodejs_readline_module"
967+
);
968+
const enabledByDate = compatibilityDate >= "2026-03-17";
954969

955-
const enabled = enabledByFlag && !disabledByFlag;
970+
const enabled = (enabledByFlag || enabledByDate) && !disabledByFlag;
956971

957972
// When enabled, use the native `readline` and `readline/promises` modules from workerd
958973
return enabled
@@ -970,22 +985,24 @@ function getReadlineOverrides({ compatibilityFlags }: Compatibility): Override {
970985
* Returns the overrides for `node:perf_hooks` (unenv or workerd)
971986
*
972987
* The native performance implementation:
973-
* - is experimental and has no default enable date
988+
* - is enabled starting from 2026-03-17
974989
* - can be enabled with the "enable_nodejs_perf_hooks_module" flag
975990
* - can be disabled with the "disable_nodejs_perf_hooks_module" flag
976991
*/
977992
function getPerfHooksOverrides({
993+
compatibilityDate,
978994
compatibilityFlags,
979995
}: Compatibility): Override {
980996
const disabledByFlag = compatibilityFlags.includes(
981997
"disable_nodejs_perf_hooks_module"
982998
);
983999

984-
const enabledByFlag =
985-
compatibilityFlags.includes("enable_nodejs_perf_hooks_module") &&
986-
compatibilityFlags.includes("experimental");
1000+
const enabledByFlag = compatibilityFlags.includes(
1001+
"enable_nodejs_perf_hooks_module"
1002+
);
1003+
const enabledByDate = compatibilityDate >= "2026-03-17";
9871004

988-
const enabled = enabledByFlag && !disabledByFlag;
1005+
const enabled = (enabledByFlag || enabledByDate) && !disabledByFlag;
9891006

9901007
return enabled
9911008
? {

packages/unenv-preset/src/runtime/polyfill/performance.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,23 @@ import {
1111
PerformanceResourceTiming,
1212
} from "node:perf_hooks";
1313

14+
// When native perf_hooks is disabled but workerd provides a web-standard
15+
// globalThis.performance (with addEventListener), unenv's polyfill defers to
16+
// the native object instead of creating a new Performance() instance.
17+
// This means Node.js-specific properties like nodeTiming are missing.
18+
// Detect this case and augment the object with the missing polyfill properties.
19+
if (!("__unenv__" in performance)) {
20+
const proto = Performance.prototype;
21+
for (const key of Object.getOwnPropertyNames(proto)) {
22+
if (key !== "constructor" && !(key in performance)) {
23+
const desc = Object.getOwnPropertyDescriptor(proto, key);
24+
if (desc) {
25+
Object.defineProperty(performance, key, desc);
26+
}
27+
}
28+
}
29+
}
30+
1431
// `performance` augments the existing workerd implementation
1532
// @ts-expect-error Node types do not match unenv
1633
globalThis.performance = performance;

0 commit comments

Comments
 (0)