Skip to content

fix(compiler): downlevel jit:true on anonymous classes too - #552

Merged
Brooooooklyn merged 1 commit into
mainfrom
fix/issue-549-anonymous-jit
Oct 6, 2026
Merged

Brooooooklyn merged 1 commit into
mainfrom
fix/issue-549-anonymous-jit

Conversation

@Brooooooklyn

@Brooooooklyn Brooooooklyn commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

Summary

jit: true on an anonymous class fell through to AOT: the jit-forced dispatch was gated on class.id.is_some(), and whole-file JIT mode skipped nameless classes entirely.

Upstream nuance verified against vendored ngtsc sources: TypeScriptReflectionHost.isClass requires node.name, so anonymous decorated classes are never visited by the TraitCompiler — jitDeclarations.add is unreachable for them and no ɵfac/ɵcmp is emitted. Their decorators still downlevel through TypeScript's own decorator emit, which produces exactly the shape this change emits (let default_1 = class ...; default_1 = __decorate([...], default_1); export default default_1;), so routing anonymous jit classes through the existing jit-forced downlevel pipeline restores the net ngtsc output — no Ivy definition, __decorate for Angular + foreign decorators — with the added benefit of ctorParameters/propDecorators statics.

This change:

  • Drops the class.id gates in both paths and synthesizes a name (default_N, TypeScript's own convention for anonymous default exports) when class.id is absent.
  • Synthesized names avoid collisions via top_level_binding_names, which collects the file's imports (import x = require(), export * as X included) and top-level declaration bindings incl. destructured patterns and vars hoisted out of nested blocks/loops.
  • jit_class_edits no longer searches for the class/abstract keywords inside decorator spans — class.span.start includes leading decorators, so a raw find("class") could land inside decorator text (e.g. @my.class(...)). For abstract classes only the abstract token after the last decorator is removed, since class expressions can't be abstract.
  • Non-jit anonymous classes keep their previous behavior.
  • A co-located @Injectable on an anonymous jit class still skips ɵfac/ɵprov — matching upstream, whose extractInjectableMetadata reads clazz.name.text and is unreachable for anonymous classes (they never reach the handler).

Test plan

  • cargo test -p oxc_angular_compiler — 41 binaries green; cargo check --workspace clean.
  • pnpm --filter @oxc-angular/compare compare --fixtures — 100% pass across all 23 categories.
  • New tests: test_jit_true_anonymous_default_export (AOT jit:true), test_jit_mode_anonymous_default_export (options.jit), test_jit_true_anonymous_name_collision (default_1 taken → default_2), test_jit_true_abstract_class (named + anonymous abstract jit:true).

Fixes #549

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Upstream's jitDeclarationRegistry has no name requirement — an
anonymous decorated class (only expressible as
`export default class {}`) still opts out of AOT and downlevels
through the JIT path. Drop the class.id gates in both the AOT
jit-forced dispatch and whole-file JIT mode, synthesizing a
default_N name (TypeScript's convention) that avoids the file's
top-level bindings.
@Brooooooklyn
Brooooooklyn force-pushed the fix/issue-549-anonymous-jit branch from 381eb35 to 79c372a Compare October 6, 2026 16:20
@Brooooooklyn
Brooooooklyn merged commit 30d90d8 into main Oct 6, 2026
10 checks passed
@Brooooooklyn
Brooooooklyn deleted the fix/issue-549-anonymous-jit branch October 6, 2026 16:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

jit:true on anonymous class falls through to AOT

1 participant