Skip to content

Commit f676e2c

Browse files
committed
chore(fmt+docs): cargo fmt --all + regen_api_docs.sh — pre-tag CI prep (v0.5.837)
Same shape as v0.5.829's catch-up but smaller scope: 6 Rust files of fmt drift from the v0.5.833→v0.5.836 perry-runtime/gc.rs + perry-hir/lower/expr_call.rs work, plus 2 small line additions in the api docs from Ralph's recent stdlib changes. Pure formatting + auto-generated artifacts; no runtime semantic change. Pre-tag prep so the lint + api-docs-drift CI gates start green at the tagged commit instead of being chased after. Local pre-tag verification: - cargo fmt --all -- --check: clean - cargo test --no-run (workspace excluding UI variants + jsruntime): clean - regen_api_docs.sh: no further drift after this commit
1 parent e84cf67 commit f676e2c

10 files changed

Lines changed: 93 additions & 92 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
Detailed changelog for Perry. See CLAUDE.md for concise summaries.
44

5+
## v0.5.837 — chore(fmt+docs): repo-wide `cargo fmt --all` (6 files of drift from v0.5.833→v0.5.836's perry-runtime/gc.rs + perry-hir/lower/expr_call.rs work) + `scripts/regen_api_docs.sh` (2 small lines reflecting Ralph's recent stdlib additions). Pure formatting + auto-generated artifacts; no runtime semantic change. Pre-tag preparation for the next release-checkpoint attempt — gets the lint + api-docs-drift CI gates green at the tagged commit instead of after the fact.
6+
57
## v0.5.836 — fix(cjs-wrap): #665 — `module.exports = { X, Y }` object-literal aggregator now forwards class identity through index files. **Symptom.** `import { RateLimiterMemory } from "rate-limiter-flexible"` returns an instance with every prototype method undefined: `typeof limiter.consume === "undefined"`, `await limiter.consume(...)` becomes `TypeError: value is not a function`. Discovered by the user's native-server boot path during the first authed POST after #609 + #639 + #652 all landed. **Architecture.** `rate-limiter-flexible/index.js` (and the published shape of many older npm packages) aggregates leaf classes via object-literal exports: `const X = require('./lib/X'); ... module.exports = { X, Y, ... };`. The v0.5.808 fix handled the property-assignment shape (`exports.X = require('./Y')`) but not the object-literal-with-shorthand shape. Pre-fix the wrap emitted `export const X = _cjs.X;` for these names — a runtime property read on the IIFE result. HIR can't see through that read to the underlying class declaration in the required file, so `new X(...)` produced an empty object with no methods. The v0.5.808 emit `export { _req_N as X };` already exists for the property-assignment shape; this commit extends it to the object-literal shape. **Fix.** New helper `extract_object_literal_exports_from_require` in `crates/perry/src/commands/compile/cjs_wrap.rs`: locates the LAST top-level `module.exports = { ... }` (later assignments win at runtime, so they win in our static analysis too), brace-balanced parses the body, splits at top-level commas (respecting nested braces/brackets/strings/templates/comments), then matches each entry as shorthand `X` or longhand `X: Y` and looks the alias up against the `const|let|var X = require('Y')` binding table. Computed keys, spreads, methods, and non-alias RHS expressions are skipped — those still need the `_cjs.X` runtime path. The detected pairs union into `named_reexport_requires` and flow through the existing `export { _req_N as X };` emit pipeline (the same one v0.5.808 wired up for `exports.X = require(...)`), so compile.rs's `Export::Named` arm walks default-import specifiers and forwards the leaf module's `"default"`-keyed class into this module's `exported_classes` under the aliased name — class identity survives the indirection. **Validation.** New 3-file Perry repro (`/tmp/test_665_main/{node_modules/test665pkg/{index.js,lib/{Child,Base}.js},main.ts}`) modeling the rate-limiter-flexible aggregator shape: `index.js` has `const Child = require('./lib/Child'); const Base = require('./lib/Base'); module.exports = { Child, Base };`, `lib/Child.js` has `class Child extends Base { ... }; module.exports = Child;`. Pre-fix output: `typeof c.greetChild: undefined / typeof c.greetBase: undefined`. Post-fix output: `function / function / child:hi / base:hi` — byte-identical to `npx tsx main.ts`. The published CJS-wrap output for the index.js confirms the emit shape: `export { _req_0 as Child };\nexport { _req_1 as Base };` instead of the previous `export const Child = _cjs.Child;\nexport const Base = _cjs.Base;`. Six new unit tests in `cjs_wrap::tests` cover shorthand, longhand, mixed-with-skipped-entries, no-require-aliases-fallthrough, last-assignment-wins, and end-to-end wrap emit. Full `cargo test -p perry cjs_wrap`: 30 tests pass (24 existing + 6 new). Full workspace test suite green. **Out of scope.** A separate `super(opts)` plumbing gap surfaces when the leaf class's constructor body sets `this.X = X` in the parent and the child calls `super(...)` — fields don't land on `this`. This is a pre-existing inheritance bug independent of #665's class-identity-forwarding ask and warrants its own focused issue. The issue's headline ("every method on the instance is undefined") is fully resolved.
68

79
## v0.5.835 — fix(compile): #629 — hard-error on unresolved namespace imports instead of silently substituting an empty-namespace stub. The original #629 fix (v0.5.723) replaced a TAG_TRUE-cascade with a typeof-"object" empty stub; the v0.5.761 and v0.5.795 follow-ups noted the downstream regression — code like `import * as fsp from "node:fs/promises"; fsp.readFile(...)` now silently no-ops at runtime because `fsp.readFile` reads undefined and Perry's `undefined(...)` call form returns undefined instead of throwing. Per the issue's acceptance criteria ("either `typeof fsp.readFile === 'function'` OR refuse to compile"), this commit takes the simpler refuse-to-compile path. **Fix.** New error arm in `crates/perry/src/commands/compile/collect_modules.rs::collect_modules` (at the previously warn-and-continue site for unresolved imports): when ANY specifier in the unresolved import is `ImportSpecifier::Namespace`, return an `anyhow::anyhow!` with the source/filename and a 3-option remediation list (switch to named imports, remove if unused, or wire perry-stdlib/perry-ext-/perry.compilePackages bindings). Named-only and bare side-effect unresolved imports still warn-and-continue per the existing behavior — those produce more pointed runtime errors at the actual missing binding rather than the silent-no-op cascade that namespace imports trigger. Native node:* paths with stdlib bindings (`node:fs`, `node:path`, `node:os`, `node:crypto`, etc.) are unaffected because `is_native_module` filters them out before this branch is reached. **Validation.** The issue's 3-line repro (`import * as fsp from "node:fs/promises"`) now exits 1 with the new error message; no binary produced. `import * as fs from "node:fs"` (native) and `import * as path from "node:path"` (native) still compile cleanly. The existing `test-files/test_issue_629.ts` was rewritten to exercise the positive native-namespace path (path.sep, path.delimiter) since the empty-stub behavior it previously asserted no longer exists. Full cargo test suite green (workspace, excluding cross-host UI crates). **Out of scope.** Populating real exports for `node:fs/promises`, `node:dns`, `node:stream/promises`, `node:console`, `node:test` (the v0.5.795-sweep follow-ups) — those would need per-module dispatch wiring; tracked as separate issues. The runtime `js_unresolved_namespace_stub` helper stays in tree as a defensive backstop in case some other code path reaches it (shouldn't be triggered in normal compile flow).

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.836
11+
**Current Version:** 0.5.837
1212

1313

1414
## TypeScript Parity Status

0 commit comments

Comments
 (0)