Repository navigation
fix: align host bindings, .d.ts injection and SSR/linker with ngtsc (#686–#696) - #697
Conversation
…l for SSR - linker: only the dev server skips `optimizeDeps.exclude`, using each environment's own list. Builds link every package, like the Angular CLI, so server bundles no longer keep ngDeclare calls from @angular/platform-server. - ssr manifest: share the plugin across environments and embed the client's built index.html (captured in generateBundle, else read from the client outDir) as index.server.html, so SSR pages load the hashed bundles instead of /src/main.ts. Warn when falling back to a stale or source page. Fixes #695 Fixes #696 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Class references in `ɵmod` lists and `hostDirectives` of the `.d.ts` now follow ngtsc's `toR3Reference` (ForceNewImport): a class of this file keeps its name, any other is its export through a namespace import (`typeof i1.Dep`, `typeof i1.default`). - The `.d.ts` has its own namespace registry, like the second ImportManager of `IvyDeclarationDtsTransform`, so its `iN` are numbered by first use there and the JS gains no import. - `ɵmod` lists come from the flattened `ModuleLists` (spreads, `const` lists, `ns.X`, `ModuleWithProviders<T>`), the same read as the partial declaration. - `generate_component_dts`, `generate_directive_dts` and `generate_ng_module_dts` take the precomputed type references. A same-file class that is not exported stays bare, like ngc. A barrel specifier is kept as written. Fixes #692 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Split inject_dts_declarations into plan_splices + merge/apply (same output) and add: - inject_dts_declarations_with_map: shifts an existing map's mappings; original tokens keep their mapping, injected text is unmapped, as in ngtsc's afterDeclarations emit. - inject_dts_declarations_with_identity_map: a map from the new code back to the original, for a bundler transform hook. Expose both over napi and as injectDtsDeclarationsWithMap / injectDtsDeclarationsWithIdentityMap in vite-plugin/utils/dts.ts. Fixes #693 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…indings
Host property names now go through one shared parse_host_property_name
(component, directive and linker paths), following Angular's
parsePropertyBinding:
- `@x` is a LegacyAnimation binding named `x` (the `@` is stripped and
added back by reify). It becomes a DomProperty, so it sorts with the
other DOM properties and adjacent triggers chain, as in ngtsc.
- `animate.*` is an Animation binding. The host convert_animations pass
turns it into an Animation create op, so it emits
ɵɵanimateEnter/Leave(function <Fn>_HostBindings_<name>_cb() {...}) in
the create block with no host vars. Host naming, host reify and
temporaries now handle that op.
- Host ingest keeps the animation kind when stripping `attr.`, so
`[@attr.foo]` stays the trigger `@foo` (Angular ingest.ts).
Temporaries in an `animate.*` callback are now declared inside it, which
also fixes the undeclared `_tmp` in template `[animate.*]` callbacks.
Fixes #688
Fixes #689
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… ngtsc - Quote every key of the partial host attributes / listeners / properties, like ngtsc's toOptionalLiteralMap. - Drop host listener / property bindings named exactly __proto__ in merge_host_bindings (full and partial), and skip __proto__ keys in the linker's host attributes / listeners / properties, like ngtsc and Angular's linker, which assign into a plain object. - Read a @HostBinding member through `this` (getSafePropertyAccessString('this', name)): `this.m`, `this['my-prop']`. Fixes #686 Fixes #687 Fixes #690 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Like ngtsc (DtsTransformRegistry keys members by source file and class node), each declaration file now only gets the members of its own source module, and its declaration map stays in step. - Bundled declarations (rolldown-plugin-dts, tsdown): a new `transform` hook (order 'pre') adds members to each `.d.ts` module before the bundler merges and renames classes, and returns an identity map so the bundler chains the dts map. It waits for the source with `this.load` when needed. `generateBundle` no longer touches chunks and never flattens by class name. - `.d.ts` assets (emitFile): matched to one source by tsc's rootDir layout (`matchDtsSource`), and their map (asset, inline base64 or `<file>.map`) is shifted. - Files a generator writes itself (vite-plugin-dts): a `writeBundle` pass (order 'post', serialized) scans `output.dir` and Vite's `build.outDir`, skips node_modules, dot-dirs and dirs at or above the root, matches files by the same path rule and updates sibling maps. - tsdown `.d.cts`: `tsdownConfig` wraps `inputOptions` so the cjs declaration pass gets a fresh, collect-only angular() instance. - README: list what is covered and what is not. Fixes #691 Fixes #692 Fixes #693 Fixes #694 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # crates/oxc_angular_compiler/tests/linker_test.rs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… embedded Follow-up to #696 (review findings on the SSR manifest plugin): - `vite build --app --watch` builds the client and server environments at the same time, so the server could embed the previous round's client index.html (a deleted hashed script) without a warning, and round 1 told users to "List `client` first" even when it already was. The client now drops its captured page at each watch round's buildStart, the server warns that in watch mode the SSR page may come from the previous client round or the source page, and the "List `client` first" advice is only given when `client` is listed after the server environment. - The fallback warning fired for every server build, even when no module uses AngularAppEngine. It is now emitted once per build, from the first transform that injects the manifest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two review findings for #689: - No angularVersion gate: host `[animate.*]` emitted ɵɵanimateEnter/Leave for Angular < 20.2, which @angular/core of that version does not export. Host ingest now keeps `animate.*` a plain property binding when the version is known and older than 20.2, so directive, component and linker output is ɵɵdomProperty / ɵɵhostProperty("animate.enter", ...) with hostVars, as ngtsc 20.1.8 / 19.2.15 emit it. Unknown versions keep the new behaviour. - The linker put static `animate.enter` / `animate.leave` host attributes into hostAttrs. They now go to the host binding job as static attributes and become ɵɵanimateEnter/Leave("value") in rf&1, after the listeners, matching Angular's babel linker output. Before v20.2 they stay in hostAttrs, like Angular's linker of that version. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rt names Review fixes for #691, #692 and #693. - #691/#693 "Bundled .d.ts now loses the JSDoc of the first class in a file with no imports" and "The new import goes between the first statement's leading JSDoc and the class": in a .d.ts with no import, the namespace imports now go before the first statement's comments, as in ngtsc (import, /** doc */, class). `/// <reference>` directives and a file header followed by a blank line (TS's detached comments) stay on top. rolldown-plugin-dts and tsdown (esm and cjs) now keep the class docs. - #693 "shift_mappings is O(splices x file size)": find each splice's line and column in one pass, move each segment by binary search, and merge the 1-field break segments while writing each line. A 2 MB .d.ts with 11000 classes went from 91 s to under 5 s in a debug test run. - #692 "Imports named by a string give typeof i1.SDir": `"s-dir" as SDir` now gives `typeof i1["s-dir"]` (ngc writes `typeof i1.s-dir`, which does not parse); `"Plain" as P` gives `i1.Plain`, as in ngc. The injection pass imports the namespace such a type query names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review fixes for #691 and #694 (vite dts plugin). - Disk pass and generateBundle assets: a .d.ts with a declaration map is matched to the source the map names, and left alone when that source is not in the graph. Without a map, one layout (output prefix + source root) is voted per output dir, and a file matches only when its path is exactly that layout for a graph source. Fixes "Disk/asset path match gives a non-graph .d.ts another module's Ivy members", "Disk pass suffix match gives a file outside the bundle another module's Ivy members" (test doubles such as src/testing/button.ts stay plain), and "No members at all when the graph's common dir is above the generator's root" / "vite-plugin-dts with entryRoot set" (entryRoot 'src' + ../shared). A tie goes to the shorter prefix, then to the root holding more graph sources (tsc's common dir); a full tie matches nothing. - Warn once per dir when none of the files a generator wrote can be matched. - Index sources by base name and match a whole dir at once ("matchDtsSource is O(files x sources)"): 2000 assets now take ~15 ms instead of ~1.5 s. - tsdown: an object inputOptions is merged with tsdown's own mergeConfig (loaded from the project), so non-cjsDts passes get exactly tsdown's plugin order and array rules (0.23: plugins [defaults, user]; <= 0.22: arrays replaced). If tsdown/config can't be loaded, the object is left alone. Fixes "tsdown inputOptions wrapper changes how tsdown merges an object-form inputOptions". - tsdown < 0.21.9 has no plugin hooks: the disk pass now does nothing there, and the README says the .d.cts needs tsdown >= 0.21.9. Fixes "README says tsdown cjs .d.cts is covered, but that needs tsdown >= 0.22" (the real floor is 0.21.9). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Angular 20.2.0 to 20.3.0 adds features: [ɵɵAnimationsFeature()] when a host attribute, property or listener is animate.leave (hasAnimationHostBinding in addFeatures). In those versions only that feature sets up the registry ɵɵanimateLeave writes to, so without it the directive throws at creation. Full compile and the linker now add it in that window; 20.3.1 dropped both and gets nothing. Also, before 20.2 a static animate.* host attribute stays in hostAttrs in full compile too, as the linker already did and as ngtsc does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… ngc The detached-header rule kept a comment group followed by a blank line above the new import. ngc edits the declaration AST, so its import is always first, and the rule could split a JSDoc from its class when a blank line followed the JSDoc. Only /// <reference> and /// <amd-…> directives stay on top now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- matchDtsSources: when layouts tie, a file matches only when every tied layout gives it the same source. The 'root holding more sources' tie-break could give a testing/ stub the members of its src/lib/ twin. - A declaration map source reached through a symlink (macOS /tmp) is compared by real path, so it still finds its graph module. - The no-match warning no longer says to turn on declarationMap when the files already have maps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
Codex Review Summary: ✅ CompletedThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9d6fa01cff
ℹ️ 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".
A .d.ts member like typeof i1["s-dir"] was not seen as a use of i1, because only "i1." was looked for, so the namespace import was left out when no other member used that module. Look for "i1[" too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
|
Codex Review: Didn't find any major issues. 👍 Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
Fixes the issues we filed while reviewing #682, #683 and #685, each checked against ngtsc 22.2 (and 19.2 / 20.1 / 20.2 where a version gate applies).
Fixes #686
Fixes #687
Fixes #688
Fixes #689
Fixes #690
Fixes #691
Fixes #692
Fixes #693
Fixes #694
Fixes #695
Fixes #696
What changed
Known limits
tscrun writes files outside the build, so no Vite plugin can reach it. The README says this (dts: declarations written by a standalonetscrun are not augmented #706).vite build --app --watchthe server embeds the page of the round before; a warning is printed each round (SSR manifest:vite build --app --watchembeds the previous round's client index.html #705)..d.tsfiles without a declaration map whose layout is ambiguous (two dirs hold files with the same paths) get no members, and a warning says so. Turning ondeclarationMapgives an exact match.Not in this PR (filed as follow-ups)
animate.*has no v20.2 gate, no ɵɵAnimationsFeature on 20.2.0–20.3.0, and a different order; host(animate.*)listeners before 20.2 #701 templateanimate.*: no v20.2 gate, noɵɵAnimationsFeatureon 20.2.0–20.3.0, order; host(animate.*)listeners before 20.2hostDirectives: partial pairs reversed; aliased / namespace / string-export references.d.tsgetsnever) #703 NgModule lists:forwardRef(ns.X),ns.X.forRoot(), spread / const lists[]/()keys accepted, andaria-*host bindings stay DOM properties #704 host bindings: empty[]/()keys,aria-*host bindingsvite build --app --watchembeds the previous round's client index.html #705 SSR manifest invite build --app --watchtscrun are not augmented #706.d.tsfrom a standalonetscrunchangeDetection: 1for v19 / v20.1 #707 non-ASCII escaping; linkerchangeDetection: 1on v19 / v20.1Tests
.d.ts, with consumertsc(skipLibCheck: false), and Vite 8--appfor SSR.🤖 Generated with Claude Code