Skip to content

fix(linker): native ?. for v22 declarations, lazy NgModule lists - #668

Merged
Brooooooklyn merged 3 commits into
mainfrom
fix/linker-v22-parity
Nov 2, 2026
Merged

Brooooooklyn merged 3 commits into
mainfrom
fix/linker-v22-parity

Conversation

@Brooooooklyn

Copy link
Copy Markdown
Member

Follow-up to #665: fixes the two linker gaps it listed.

1. ?. links the legacy way for v22 declarations

Our linker always expanded a?.b to a == null ? null : a.b (gives null). Angular 22's linker uses native ?. (gives undefined) for declarations at v22 or later.

Angular linker:  legacyOptionalChaining = major < 22 && version !== PLACEHOLDER
                 (the option exists from compiler-cli 22.0.0-next.10)

ours now:        app linker < 22.0.0-next.10 (LinkOptions.angularVersion) ──> legacy
                 declaration version < 22                                ──> legacy
                 declaration version ≥ 22 (prerelease too)               ──> native ?.
  • Applies to component templates and to component and directive host bindings (the only linked code Angular compiles with this flag).
  • The version is read like npm semver: a quoted "version" key, surrounding spaces and a leading v are accepted.
  • An optional chain that tags a template literal is printed as (ctx.a?.b)t, the way Babel prints it. `ctx.a?.b`t is a syntax error. This bug was already in full mode at v22; native ?. would have brought it into the linker.

2. NgModule ↔ component import cycle throws

before:  ɵɵdefineNgModule({ type: M, declarations: [App, D], ... })        ← reads App while mod.mjs loads
after:   ɵɵdefineNgModule({ type: M, declarations: () => [App, D], ... })  ← read later
  • By default, Angular's linker leaves declarations / imports / exports out. We keep them: components compiled by Oxc (ɵɵgetComponentDepsFactory) read an NgModule's exports at runtime. Without them you get NG0302.
  • Every reader in @angular/core 22 (DepsTracker, NgModuleRef, TestBed, JIT checks) goes through maybeUnwrapFn, so function lists are safe. This was checked with real runs: NgModule and standalone apps, bootstrap, router loadChildren, importProvidersFrom, TestBed, dev mode on and off.
  • bootstrap stays eager unless a field is a forward declaration, as in Angular. Forward declarations now use Angular's () => [...] form instead of function() { return [...]; }.

Verification

  • Linker tests cover the version rules and the app-version gate, plus tagged templates, the semver forms, and lazy lists for sequences and objects. Each fails without its fix.
  • New napi test linker-ng-module-scope-runtime.test.ts:
    • The cycle loads in either order, with Angular's linker and with ours. This is checked in a plain node process, because Vitest's module runner hides TDZ errors.
    • A component compiled by Oxc still gets what a linked NgModule exports.
  • SSR renders now match Angular's linker:
    • {{ o?.a?.b === undefined }} gives true (was false).
    • The module ↔ component cycle renders (was Cannot access 'App' before initialization).
  • A review pass compared ~105 template cases and 11 host cases with Angular 22.2.3's linker. Every ?. result matched once the tagged-template fix was in.
  • cargo tests (with and without cross_file_elision), napi tests (419), conformance 1264/1264.

Not in this PR

  • ɵinj.imports still copies exports as written, like ngtsc's local compilation. So a module that exports a class from the cycle still throws. ngtsc's full build keeps only NgModules there, but that filter needs type info and the code is shared with full mode.
  • A 0.0.0-PLACEHOLDER declaration counts as the latest version, as our standalone and OnPush defaults already do. Angular's shipped linker reads it as v0.
  • Full-mode ɵɵsetNgModuleScope is eager and not guarded by ngJitMode.

🤖 Generated with Claude Code

Brooooooklyn and others added 2 commits November 2, 2026 11:27
…sts lazily

- Like Angular's linker, `?.` in the template and host bindings of a
  component or directive declared at v22 or later links natively, giving
  `undefined`. Before v22, or with a linker before v22.0.0-next.10, it
  stays `a == null ? null : a.b`.
- `ɵɵdefineNgModule`'s declarations, imports and exports are
  `() => [...]`: a module and a component file that import each other load
  in either order. Angular's linker leaves them out; we keep them for
  components that read them at runtime.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Wrap an optional chain that tags a template literal in parentheses,
  `(ctx.a?.b)`t``, as Babel prints it: `ctx.a?.b`t`` is a syntax error.
- Read a declaration's `version` like Angular's linker, with npm's
  semver: a quoted key, surrounding spaces and a leading `v` too.
- Return a sequence or an object literal from a lazy NgModule list in
  parentheses.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Brooooooklyn

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-02T03:54:14.395396Z ad66fe5 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Already looking forward to the next diff.

Reviewed commit: 92acfe6053

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Brooooooklyn

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: ad66fe5111

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@Brooooooklyn
Brooooooklyn merged commit 45043a6 into main Nov 2, 2026
10 checks passed
@Brooooooklyn
Brooooooklyn deleted the fix/linker-v22-parity branch November 2, 2026 04:16
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.

1 participant