From 99d3bd1ff043babffd5c7a86822346ca19eb60e5 Mon Sep 17 00:00:00 2001 From: Andrew DiZenzo Date: Sun, 31 May 2026 18:06:30 +0000 Subject: [PATCH] fix(constants): expose default alias export --- crates/perry-api-manifest/src/entries.rs | 1 + crates/perry-hir/src/lower/lower_expr.rs | 10 +++- crates/perry-hir/src/lower/module_decl.rs | 10 +++- .../perry-runtime/src/object/native_module.rs | 57 +++++++++++++------ crates/perry-runtime/src/process.rs | 7 +++ docs/api/perry.d.ts | 21 +++++-- docs/src/api/reference.md | 11 +++- .../constants/imports/default-export.ts | 12 ++++ 8 files changed, 103 insertions(+), 26 deletions(-) create mode 100644 test-parity/node-suite/constants/imports/default-export.ts diff --git a/crates/perry-api-manifest/src/entries.rs b/crates/perry-api-manifest/src/entries.rs index 2a3446daede..56479d61a8f 100644 --- a/crates/perry-api-manifest/src/entries.rs +++ b/crates/perry-api-manifest/src/entries.rs @@ -2680,6 +2680,7 @@ pub static API_MANIFEST: &[ApiEntry] = &[ // Deprecated `node:constants` flat alias. It mirrors the fs/os/crypto // constants that Perry already exposes under module-specific // `*.constants` namespaces. + property("constants", "default"), property("constants", "F_OK"), property("constants", "R_OK"), property("constants", "W_OK"), diff --git a/crates/perry-hir/src/lower/lower_expr.rs b/crates/perry-hir/src/lower/lower_expr.rs index 121576959da..822c95e9cb7 100644 --- a/crates/perry-hir/src/lower/lower_expr.rs +++ b/crates/perry-hir/src/lower/lower_expr.rs @@ -113,7 +113,15 @@ fn is_known_global_identifier_name(name: &str) -> bool { fn is_cjs_style_native_default_import(module_name: &str) -> bool { matches!( module_name, - "async_hooks" | "events" | "os" | "path" | "querystring" | "sys" | "url" | "util" + "async_hooks" + | "constants" + | "events" + | "os" + | "path" + | "querystring" + | "sys" + | "url" + | "util" ) } diff --git a/crates/perry-hir/src/lower/module_decl.rs b/crates/perry-hir/src/lower/module_decl.rs index c4131931ae5..49fa624fee8 100644 --- a/crates/perry-hir/src/lower/module_decl.rs +++ b/crates/perry-hir/src/lower/module_decl.rs @@ -17,7 +17,15 @@ use crate::ir::*; fn is_cjs_style_native_default_import(module_name: &str) -> bool { matches!( module_name, - "async_hooks" | "events" | "os" | "path" | "querystring" | "sys" | "url" | "util" + "async_hooks" + | "constants" + | "events" + | "os" + | "path" + | "querystring" + | "sys" + | "url" + | "util" ) } diff --git a/crates/perry-runtime/src/object/native_module.rs b/crates/perry-runtime/src/object/native_module.rs index c6972289463..d2b1796df0c 100644 --- a/crates/perry-runtime/src/object/native_module.rs +++ b/crates/perry-runtime/src/object/native_module.rs @@ -999,6 +999,24 @@ fn deprecated_constants_keys() -> &'static [&'static [u8]] { DEPRECATED_CONSTANTS_KEYS } +fn deprecated_constants_namespace_keys() -> &'static [&'static [u8]] { + use std::sync::OnceLock; + static MERGED: OnceLock> = OnceLock::new(); + MERGED + .get_or_init(|| { + let keys = deprecated_constants_keys(); + let mut v: Vec<&'static [u8]> = Vec::with_capacity(keys.len() + 1); + for &k in keys { + if k == b"defaultCoreCipherList" { + v.push(b"default"); + } + v.push(k); + } + v + }) + .as_slice() +} + #[cfg(test)] mod tests { use super::deprecated_constants_keys; @@ -1159,7 +1177,8 @@ pub(crate) fn native_module_enumerable_keys(module_name: &str) -> Option<&'stati "path.default" => Some(PATH_DEFAULT_KEYS), "path.posix" | "path.win32" => Some(&[b"_makeLong"]), "fs" => Some(FS_NAMESPACE_KEYS), - "constants" => Some(deprecated_constants_keys()), + "constants" => Some(deprecated_constants_namespace_keys()), + "constants.default" => Some(deprecated_constants_keys()), "querystring" => Some(QUERYSTRING_NAMESPACE_KEYS), "querystring.default" => Some(QUERYSTRING_DEFAULT_KEYS), "os" => Some(OS_NAMESPACE_KEYS), @@ -1205,6 +1224,7 @@ pub(crate) fn native_module_has_enumerable_key(module_name: &str, key: &str) -> fn cjs_default_base_module(module_name: &str) -> Option<&'static str> { match module_name { "async_hooks.default" => Some("async_hooks"), + "constants.default" => Some("constants"), "os.default" => Some("os"), "path.default" => Some("path"), "querystring.default" => Some("querystring"), @@ -1217,6 +1237,7 @@ fn cjs_default_base_module(module_name: &str) -> Option<&'static str> { fn cjs_default_namespace_name(module_name: &str) -> Option<&'static str> { match module_name { "async_hooks" => Some("async_hooks.default"), + "constants" => Some("constants.default"), "os" => Some("os.default"), "path" => Some("path.default"), "querystring" => Some("querystring.default"), @@ -1234,7 +1255,7 @@ fn create_cjs_default_namespace(module_name: &str) -> Option { fn cjs_default_export_value(module_name: &str) -> Option { match module_name { "events" => Some(bound_native_callable_export_value("events", "EventEmitter")), - "async_hooks" | "os" | "path" | "querystring" | "url" | "util" => { + "async_hooks" | "constants" | "os" | "path" | "querystring" | "url" | "util" => { create_cjs_default_namespace(module_name) } _ => None, @@ -1259,6 +1280,7 @@ fn should_cache_native_module_namespace(module_name: &str) -> bool { | "async_hooks" | "async_hooks.default" | "constants" + | "constants.default" | "events" | "fs.constants" | "os" @@ -3973,20 +3995,23 @@ pub(crate) unsafe fn get_native_module_constant( "default" if !is_cjs_default_object => cjs_default_export_value("querystring"), _ => None, }, - "constants" => fs_const(property) - .or_else(|| fs_const_tail(property)) - .or_else(|| os_signal_const(property)) - .or_else(|| os_errno_const(property)) - .or_else(|| os_priority_const(property)) - .or_else(|| os_dlopen_const(property)) - .or_else(|| crypto_const(property)) - .or_else(|| { - if property == "defaultCoreCipherList" { - Some(str_val(DEFAULT_CORE_CIPHER_LIST)) - } else { - None - } - }), + "constants" => match property { + "default" if !is_cjs_default_object => cjs_default_export_value("constants"), + _ => fs_const(property) + .or_else(|| fs_const_tail(property)) + .or_else(|| os_signal_const(property)) + .or_else(|| os_errno_const(property)) + .or_else(|| os_priority_const(property)) + .or_else(|| os_dlopen_const(property)) + .or_else(|| crypto_const(property)) + .or_else(|| { + if property == "defaultCoreCipherList" { + Some(str_val(DEFAULT_CORE_CIPHER_LIST)) + } else { + None + } + }), + }, "path" => match property { "default" if !is_cjs_default_object => cjs_default_export_value("path"), "sep" => { diff --git a/crates/perry-runtime/src/process.rs b/crates/perry-runtime/src/process.rs index 6dbd079eb42..864f6900ac9 100644 --- a/crates/perry-runtime/src/process.rs +++ b/crates/perry-runtime/src/process.rs @@ -549,6 +549,13 @@ pub extern "C" fn js_process_get_builtin_module(id: f64) -> f64 { let Some(module_name) = supported_builtin_module_name(name) else { return f64::from_bits(crate::value::TAG_UNDEFINED); }; + if module_name == "constants" { + let cjs_default = "constants.default"; + return crate::object::js_create_native_module_namespace( + cjs_default.as_ptr(), + cjs_default.len(), + ); + } crate::object::js_create_native_module_namespace(module_name.as_ptr(), module_name.len()) } diff --git a/docs/api/perry.d.ts b/docs/api/perry.d.ts index 92d92da58bf..cd1dfc59ec8 100644 --- a/docs/api/perry.d.ts +++ b/docs/api/perry.d.ts @@ -1,6 +1,6 @@ // Auto-generated from Perry's API manifest (#465). Do not edit by hand. // Source: perry-api-manifest::API_MANIFEST -// Coverage: 2403 entries across 104 modules +// Coverage: 2408 entries across 104 modules type PerryU32 = number & { readonly __perryU32?: never }; type PerryU64 = number & { readonly __perryU64?: never }; @@ -567,12 +567,16 @@ declare module "constants" { /** stdlib */ export const O_CREAT: any; /** stdlib */ + export const O_DIRECT: any; + /** stdlib */ export const O_DIRECTORY: any; /** stdlib */ export const O_DSYNC: any; /** stdlib */ export const O_EXCL: any; /** stdlib */ + export const O_NOATIME: any; + /** stdlib */ export const O_NOCTTY: any; /** stdlib */ export const O_NOFOLLOW: any; @@ -583,8 +587,6 @@ declare module "constants" { /** stdlib */ export const O_RDWR: any; /** stdlib */ - export const O_SYMLINK: any; - /** stdlib */ export const O_SYNC: any; /** stdlib */ export const O_TRUNC: any; @@ -625,6 +627,8 @@ declare module "constants" { /** stdlib */ export const RSA_X931_PADDING: any; /** stdlib */ + export const RTLD_DEEPBIND: any; + /** stdlib */ export const RTLD_GLOBAL: any; /** stdlib */ export const RTLD_LAZY: any; @@ -651,8 +655,6 @@ declare module "constants" { /** stdlib */ export const SIGILL: any; /** stdlib */ - export const SIGINFO: any; - /** stdlib */ export const SIGINT: any; /** stdlib */ export const SIGIO: any; @@ -663,12 +665,18 @@ declare module "constants" { /** stdlib */ export const SIGPIPE: any; /** stdlib */ + export const SIGPOLL: any; + /** stdlib */ export const SIGPROF: any; /** stdlib */ + export const SIGPWR: any; + /** stdlib */ export const SIGQUIT: any; /** stdlib */ export const SIGSEGV: any; /** stdlib */ + export const SIGSTKFLT: any; + /** stdlib */ export const SIGSTOP: any; /** stdlib */ export const SIGSYS: any; @@ -823,6 +831,9 @@ declare module "constants" { /** stdlib */ export const X_OK: any; /** stdlib */ + const _default: any; + export default _default; + /** stdlib */ export const defaultCoreCipherList: any; } diff --git a/docs/src/api/reference.md b/docs/src/api/reference.md index ac5947a6b07..5ce144f384f 100644 --- a/docs/src/api/reference.md +++ b/docs/src/api/reference.md @@ -2,7 +2,7 @@ This page is auto-generated from Perry's compile-time API manifest (`perry-api-manifest::API_MANIFEST`). It is the source of truth for what `perry compile` accepts; references to symbols not listed here produce `R005 UnimplementedApi` (issue #463). Stubs (#464) are flagged ⚠ — they link cleanly but no-op at runtime on the chosen target. -Total: 2403 entries across 104 modules. +Total: 2408 entries across 104 modules. ## Modules @@ -515,15 +515,16 @@ Total: 2403 entries across 104 modules. - `OPENSSL_VERSION_NUMBER` - `O_APPEND` - `O_CREAT` +- `O_DIRECT` - `O_DIRECTORY` - `O_DSYNC` - `O_EXCL` +- `O_NOATIME` - `O_NOCTTY` - `O_NOFOLLOW` - `O_NONBLOCK` - `O_RDONLY` - `O_RDWR` -- `O_SYMLINK` - `O_SYNC` - `O_TRUNC` - `O_WRONLY` @@ -544,6 +545,7 @@ Total: 2403 entries across 104 modules. - `RSA_PSS_SALTLEN_DIGEST` - `RSA_PSS_SALTLEN_MAX_SIGN` - `RSA_X931_PADDING` +- `RTLD_DEEPBIND` - `RTLD_GLOBAL` - `RTLD_LAZY` - `RTLD_LOCAL` @@ -557,15 +559,17 @@ Total: 2403 entries across 104 modules. - `SIGFPE` - `SIGHUP` - `SIGILL` -- `SIGINFO` - `SIGINT` - `SIGIO` - `SIGIOT` - `SIGKILL` - `SIGPIPE` +- `SIGPOLL` - `SIGPROF` +- `SIGPWR` - `SIGQUIT` - `SIGSEGV` +- `SIGSTKFLT` - `SIGSTOP` - `SIGSYS` - `SIGTERM` @@ -643,6 +647,7 @@ Total: 2403 entries across 104 modules. - `UV_FS_SYMLINK_JUNCTION` - `W_OK` - `X_OK` +- `default` - `defaultCoreCipherList` ## `cron` diff --git a/test-parity/node-suite/constants/imports/default-export.ts b/test-parity/node-suite/constants/imports/default-export.ts new file mode 100644 index 00000000000..6a7c6424d6f --- /dev/null +++ b/test-parity/node-suite/constants/imports/default-export.ts @@ -0,0 +1,12 @@ +import constantsDefault from "node:constants"; +import * as constantsNs from "node:constants"; +import process from "node:process"; + +const builtin = process.getBuiltinModule("constants"); + +console.log("namespace has default:", Object.keys(constantsNs).includes("default")); +console.log("namespace default identity:", constantsNs.default === constantsDefault); +console.log("builtin default identity:", builtin === constantsDefault); +console.log("default lacks default key:", !Object.keys(constantsDefault).includes("default")); +console.log("default value works:", constantsDefault.F_OK, typeof constantsDefault.F_OK); +console.log("namespace value same:", constantsNs.F_OK === constantsDefault.F_OK);