Skip to content

Commit 981b324

Browse files
proggeramlugRalph Küpper
andauthored
fix(runtime): #5437 — CJS-interop probe on module-default wrapper must not auto-call it (fixes #5546 boot crash) (#5553)
* fix(runtime): #5437 Next.js W6 wall — CJS-interop probe on module-default wrapper must not auto-call it The W6 wrapper-resolve fallback in js_object_get_field_by_name auto-calls a registered module-default wrapper closure on a property miss to obtain module.exports. For a CJS module whose module.exports IS a plain function (next/dist/compiled/debug -> createDebug), the registered wrapper is that function itself. _interop_require_default(require('debug')) reads .__esModule off it: the auto-call ran createDebug() with no args -> enabled(undefined) -> undefined.length -> 'Cannot read properties of undefined (reading length)', crashing the Next.js server at boot. Short-circuit the two CJS-interop probe keys on a registered wrapper: - .__esModule -> undefined (a function-export CJS module is not an ES module) - .default -> the wrapper closure itself (interop default of a non-ESM module is module.exports) Both also excluded from the auto-call exclusion list. The genuine W6 SharedCacheControls path (non-default member reads) is unaffected. Bundle: .length-on-undefined throw gone; Next.js server now BINDS (was HTTP 000 boot-exit) and serves; next wall = request-time uS_constructor new uw.SharedCacheControls captured mis-box (separate, pre-documented). Regression test: module_default_wrapper_interop_probe_does_not_call_wrapper. * chore: bump 0.5.1200 → 0.5.1201 + CHANGELOG for #5546 boot-crash fix --------- Co-authored-by: Ralph Küpper <ralph2@skelpo.com>
1 parent 0f90fc3 commit 981b324

6 files changed

Lines changed: 171 additions & 81 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## v0.5.1201 — fix(runtime): #5437 — CJS-interop probe on a module-default wrapper must not auto-call it (boot-crash fix for #5546)
2+
3+
Follow-up to #5546. That change registers CJS module-default wrapper closures and, on a property miss, auto-calls the wrapper (`js_closure_call0`) to obtain `module.exports`. But for a CJS module whose `module.exports` **is a function** (e.g. `debug` → `createDebug`), the wrapper *is* that function, and the ubiquitous `_interop_require_default(require("debug"))` interop reads `.__esModule` off it → property miss → the fallback **called the module's function with no args** as a side effect → e.g. `enabled(undefined)` → `undefined.length` → threw at module-init (server exits at boot). This regressed the Next.js bundle from serving (HTTP 500) to crashing at boot (HTTP 000) and could break other SWC/Babel-compiled-CJS programs.
4+
5+
Fix (runtime-only, `object/field_get_set.rs`): on a registered module-default wrapper, short-circuit the two CJS-interop probe keys **before** the auto-call — `.__esModule` → `undefined` (a function-export CJS module is not an ES module), `.default` → the wrapper itself (interop default of a non-ESM module is `module.exports`); both also added to the auto-call exclusion list. Genuine non-`default` member reads (the #5437 `SharedCacheControls` path) are unaffected. Regression test `module_default_wrapper_interop_probe_does_not_call_wrapper` (a wrapper that `panic!`s if called, asserting the probe keys resolve without invoking it). Bundle: server boots/serves again (the `.length` boot crash is gone).
6+
17
## v0.5.1200 — fix(codegen+runtime): #5437 Next.js W6 — captured CJS module-default wrapper closure resolves exports
28

39
The Next.js standalone (turbopack `app-page-turbo`) render threw `TypeError: undefined is not a constructor` at `new uw.SharedCacheControls(...)` inside the `IncrementalCache` constructor. Root: `let uw = require(".../shared-cache-controls.external.js")` is a lazy/function-local require whose value-read takes the `imported_vars` default-getter path (`dyn_extern_i18n.rs`), which for a CJS `Object.defineProperty(exports, …)` module returns the module-default **wrapper closure** (the un-called IIFE), not `module.exports`. The class captured that closure by-value into a `__perry_cap_` field, so `uw.SharedCacheControls` read the closure → `undefined` → `new undefined()` threw. (Diverged only at giant-bundle scale; the import getter resolves the real object everywhere else.)

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
88

99
Perry is a native TypeScript compiler written in Rust that compiles TypeScript source code directly to native executables. It uses SWC for TypeScript parsing and LLVM for code generation.
1010

11-
**Current Version:** 0.5.1200
11+
**Current Version:** 0.5.1201
1212

1313

1414
## TypeScript Parity Status

0 commit comments

Comments
 (0)