When compiling under standalone compilation, generated runtime static declarations (static ɵfac and static ɵprov) are emitted into preprocessed TypeScript files and visible to the compiler during semantic typechecking.
When a subclass extends a base class where the subclass is not structurally subtype-compatible with the superclass (such as differing generic type constraints, contravariant method parameters, or EventEmitter<this>), TypeScript's class static side heritage check (TS2417) fails because ɵɵFactoryDeclaration<T> and ɵɵInjectableDeclaration<T> structurally referenced the instance type T.
This change updates ɵɵFactoryDeclaration to return any instead of T, and sets factory return and value types in ɵɵInjectableDeclaration to any. This decouples static side inheritance from T, resolving TS2417 errors across subclassed components and injectables while preserving .d.ts metadata indexing and assignability to ɵɵdefineInjectable. This brings ɵfac and ɵprov into alignment with other Ivy declarations (ɵcmp, ɵdir, ɵpipe, ɵinj), which already treat their generic parameters as phantom metadata.
(cherry picked from commit 66d505e287)
Foreign components are only supported in AOT mode. Using them in
JIT mode previously resulted in silent failures or confusing runtime
errors (such as unknown element errors or crashed template ingestion).
This commit adds explicit validation in JIT compilation:
- Throws an error during component compilation if `foreignImports` is
specified on `@Component`.
- Throws an error during standalone import verification if a foreign
component is mistakenly passed to `@Component.imports`.
(cherry picked from commit 915a03ae85)
For external templates (using `templateUrl`), primary diagnostics are
reported against the synthetic `ts.SourceFile` representing the HTML
template document. However, secondary related messages (such as those in
`foreign_component.ts` and `oob.ts`) were explicitly passing the
component's TypeScript file as `sourceFile`.
Because the character offsets (`start` and `end`) originate from the
HTML template AST, associating them with the TypeScript source file
caused IDEs and CLI diagnostics to map HTML offsets onto the `.ts` file,
resulting in corrupt or out-of-bounds source locations.
This commit resolves the issue by:
1. Making `sourceFile` optional in `makeTemplateDiagnostic` and related
checker interfaces (`TemplateTypeChecker`, `TemplateContext`).
2. Defaulting `relatedMessage.sourceFile` to the template's source file
(`sf` for external/indirect templates, or the component `.ts` file
for direct inline templates) when not explicitly provided.
3. Removing explicit `sourceFile: this.sourceMapping.node.getSourceFile()`
mappings from `foreign_component.ts` and DOM element checks in
`oob.ts`, allowing them to automatically resolve to the template file.
4. Adding unit test coverage for external templates encountering foreign
component conflicts with related messages.
(cherry picked from commit a46292af26)
This started from a real production crash trace:
TypeError: Cannot read properties of null (reading 'nextSibling')
Traced through a minified bundle back to siblingAfter() in
packages/core/src/hydration/node_lookup_utils.ts. It walks forward a
fixed number of DOM siblings during hydration, based on how many the
server rendered. Its only guard, validateSiblingNodeExists(), is
gated behind ngDevMode and stripped from production builds. If the
client DOM has fewer real siblings than the server-serialized data
expects — client/server render diverged, or something outside
Angular (a browser extension, an injected script) removed a node —
the loop ran fully unguarded in production: currentNode went null
partway through, and the next iteration's currentNode.nextSibling
threw a raw, uncoded TypeError instead of a coded, debuggable one.
Reproduced first, not just theorized: added a test that server-
renders a @for loop with 3 items, removes 2 of them before hydration
runs, and confirmed it actually throws that exact TypeError against
the original code before touching anything.
Fix: add a null check at the top of the loop, matching the pattern
already used for the existing NG0500/NG0502 hydration checks in
element.ts — the dev-mode check runs first as before (a no-op in
production), and the new check is a pure safety net that only fires
once that dev-mode check has already been compiled away. In dev mode
this is provably dead code, so nothing about dev behavior changes.
Coded as NG0501 (HYDRATION_MISSING_SIBLINGS, already existed).
While investigating, found and fixed two more call sites with the
exact same shape of bug — a dev-only check that leaves production
completely unguarded:
- navigateToNode() (same file): walks an explicit "firstChild /
nextSibling" navigation path recorded for nodes that can't rely on
simple sibling-walking (e.g. content right after an <ng-content>
slot). Its only guard was also ngDevMode-only. Added the same
production safety net, both mid-loop (the raw-crash case) and
post-loop (where it could previously return null silently despite
the function's return type promising a non-null RNode). New code:
NG0509 (HYDRATION_MISSING_NODE_ON_PATH).
- getParentRElement() (render3/node_manipulation.ts): typed its
tNode parameter as always non-null, but a real production trace
showed it can be null at runtime, crashing on tNode.parent with no
useful information. Widened the type to TNode | null and added a
guard that throws a coded error instead. New code: NG0510
(PARENT_NODE_NOT_FOUND).
Unlike the other two, this one isn't gated behind ngDevMode at all
in the original code — it reproduces unconditionally. Decoded the
actual production minified stack trace against this source
(matched every frame character-for-character) to find the real
cause instead of guessing: an @if/@switch branch's content is its
own embedded template with its own TView, built lazily the first
time that branch renders. If an error interrupts that first pass —
here, a hydration mismatch on the branch's second child, after the
first child's TNode was already created — TView.firstCreatePass
still gets flipped to false in render.ts's `catch` block before the
error propagates, permanently marking that TView as corrupted.
Unlike a component's TView (rebuilt from scratch next time via
getOrCreateComponentTView()'s incompleteFirstPass check), nothing
rebuilds an embedded view's TView. The next time that exact branch
is selected again, its instructions read straight from the
corrupted tView.data instead of creating a fresh TNode, and any
node past the interruption point is still null.
The real test added for this (full_app_hydration_spec.ts) exercises
that whole chain for real: a hydration mismatch on an @if branch's
second child, then re-entering the same branch. Confirmed it throws
the exact byte-for-byte production error message ("Cannot read
properties of null (reading 'parent')") without the fix, and the
coded NG0510 with it. This replaces an earlier synthetic unit test
that just cast a variable to null to bypass the type system rather
than reproducing the actual failure.
Note: a more thorough fix would rebuild the corrupted embedded TView
itself (mirroring getOrCreateComponentTView()), which does make the
crash disappear — but doing that surfaces a second, separate bug:
the aborted first attempt's partially-created LView/DOM is never
torn down, so the retried branch's content gets appended alongside
leftover orphaned nodes instead of replacing them, corrupting
content silently instead of crashing loudly. That's a materially
bigger fix (proper LView cleanup after a mid-creation exception) and
is intentionally out of scope here; this commit keeps the narrower,
safe fix (a clear coded error) rather than trading a loud crash for
silent data corruption.
Along the way, deduplicated describeDomNode(), which existed as two
near-identical private copies (one in this file's own NG0500 check,
one in platform-browser's dom_renderer.ts insertBefore check) and
moved it into core's hydration/error_handling.ts, shared via the
private ɵdescribeDomNode export.
That function was then removed from every production code path
entirely, per review feedback: computing a human-readable node
description is debug-oriented work that shouldn't ship unconditionally
in production bundles, regardless of how cheap it is — production
should get the bare coded error only, the same as everywhere else in
this file. All three fixes above follow that: ngDevMode && 'message'
in production, no computed description. Confirmed the removal
actually shrinks output, not just assumed it: regenerated all 8
affected bundling symbol goldens, and each one shows exactly one
symbol removed — "describeDomNode" — and nothing else.
(cherry picked from commit 83f7695b2e)
An example-config.json marked a directory as a doc example and told aio's
example tooling which boilerplate to use and what to run against it. #56496
removed that tooling in June 2024. Eight of these fifteen files are empty and no
code has read any of them since; the name survives only in the zip and
StackBlitz exclude lists, which skip it rather than open it.
(cherry picked from commit ddfd8a8e9b)
The `built-in-directives` and `structural-directives` example apps are no
longer referenced. Two guide rewrites moved their pages to inline code fences
and dropped the last references without removing the apps: #69134 for
structural directives, and #69822 for built-in directives, whose page is gone
entirely. Both apps sat inside the `embeddable` glob, so they were still
compiled as preview components on every build.
The example e2e suites are excluded from every consumer by design: previews
ignore `*.e2e-spec.ts` and `*.po.ts`, and the zip and StackBlitz pipelines
both list the suffix in `EXCLUDE_FILES`. They could not run in any case,
since every spec declares a `driver` and never assigns it before calling
`driver.get('')`, left over from an abandoned protractor to selenium
migration.
The eslint configuration in the same directory is also unused. Nothing in the
repository depends on eslint, and `tsconfig.eslint.json` extends a path that
no longer exists.
(cherry picked from commit 90fc9475f2)
`guide/http/security` and `reference/concepts` both still exist as content but
neither is routed, so each falls through to the 404 shell.
`guide/http/security` was navigable from November 2023 until #54365 removed its
entry in February 2024. #55029 then copied its XSRF sections into the security
guide, which already covered XSSI, and #55060 repointed the remaining links, so
it now redirects to `/best-practices/security`.
`reference/concepts` was added by #54365 and removed by #58694 in November
2024, leaving it navigable for nine months without a redirect. The same commit
also removed `guide/ngmodules`, the route its only card linked to, so both now
redirect to `/guide/ngmodules/overview`, alongside the four `guide/ngmodules/*`
paths already redirected there.
Both pages are removed, along with the Bazel package that existed only to build
the concepts page, and the stale `llms-list.md` entry for the HttpClient page.
The security guide takes its place in that list, so `llms-full.txt` keeps its
XSSI coverage and picks up CSP, Trusted Types and sanitization with it.
(cherry picked from commit 7da60d1920)
The `msapplication` tile configuration serves no purpose now, so the
`browserconfig.xml` it points at, the five tile images and the two meta tags
in `index.html` are removed together.
`shared-docs/icons/twitter.svg` is also unused, left behind when the footer
moved to X and Bluesky. Its three siblings in that directory are all still
referenced.
(cherry picked from commit a67d4027e2)
Avoid trimming urlStr with String.prototype.trim() in resolveUrl to ensure URL parsing and resolution align with the WHATWG URL standard.
(cherry picked from commit 3e924cc8db)
Non-finite values all used NumberSymbol.Infinity, so formatNumber(NaN)
rendered as infinity. Locale data already defines NumberSymbol.NaN.
(cherry picked from commit 7596548e9b)
The `pack-and-upload-artifact` action from `dev-infra` recently added conditional steps that require `triggering-label` and `angular-robot-key` to be passed. Without these inputs, the internal action steps were silently skipped, resulting in no artifact being uploaded. This caused the subsequent deploy workflow to fail when it couldn't find the `adev-preview` artifact.
(cherry picked from commit 3be5facf04)
Use a custom logger that prefixes the messages with `[Angular DevTools]`;
Add ability for dev-only log messages;
Add Angular-DevTools-specific `Error`;
(cherry picked from commit 1371c946aa)
This fix ensures that metadata is properly retained when processing
strict standalone component errors for improved error diagnostics.
(cherry picked from commit 74b294cd51)
Store transfer state and serialization callbacks in null-prototype dictionaries, and only read values that belong to the store. This keeps special state keys from changing lookup behavior or exposing inherited cache entries.
Fixes#70265
(cherry picked from commit 168a324cce)
Infer URL parameter values as strings and type preloading work as Observable<void> to match the values produced by these internal flows.
(cherry picked from commit f275d289a0)
When a derived class inherits from a base class, TypeScript enforces that static
properties on the derived class are assignable to those on the base class.
Because `ɵɵFactoryDeclaration<T, ...>` had a return type of `T`, static `ɵfac`
members across inheritance hierarchies could result in type incompatibilities
(for example, when dealing with generics or differing class shapes where the
derived factory return type is not compatible with the base class factory).
Updating the return type of `ɵɵFactoryDeclaration` from `T` to `any` avoids
these static type conflicts across inheriting classes.
(cherry picked from commit 0d7ae16350)
The semantics of optional chaining changes when there are extra parenthesis. We need to make sure that we do not introduce some unnecessary ones.
fixes#70143
(cherry picked from commit e9ba39d671)
Two headings on the same page can generate the same anchor id, and every
link to it then resolves to whichever comes first. Nothing caught this
because the route manifest keeps anchors in a `Set`, so a repeat collapses
into one entry before any check runs, and the existing link validation only
asks whether an anchor exists, which a duplicate satisfies.
The generator now inspects each page's headings while they are still an
ordered list and fails with the offending pages and anchors. The scanning
and the duplicate check move to `heading.mts` so they are covered by tests
next to `getIdFromHeading`, which should keep this from coming back.
(cherry picked from commit 5e8a70a010)
Eight guides have headings that generate the same anchor id twice or more.
Because an id resolves to the first element that claims it, the table of
contents lists two entries pointing at the same place, the second section
cannot be reached from the ToC at all, and the copy link button on the
later heading hands out a URL for the earlier one.
On `guide/forms/reactive-forms` the two "Display the component" steps sit
5176px apart and both ToC entries scroll to the first.
Give the later heading on each page an explicit id with the `{#custom-id}`
syntax the pipeline already supports. Only later duplicates are changed, so
every anchor that resolves today keeps pointing at the same heading.
(cherry picked from commit 698a14d735)
Most of these came over from the angular.io docs and were orphaned as adev
rewrote or dropped the guides that used them. Two are newer: a devtools
screenshot that was added but never referenced, and the logo left behind
when the Firebase Studio launcher was removed. All of them are still
copied into the build and served from angular.dev.
No markdown, template, stylesheet, TypeScript file or build target
references any of them, by filename or through a path built at runtime,
and the images are globbed into the build rather than listed, so nothing
else needs updating. Removes 78 files, 5.15 MB in total, and empties
nineteen directories.
(cherry picked from commit 355a9d83e3)
The link to the Wikipedia article on abstract syntax trees spelled the
underscores as asterisks, which 404s. The same link two paragraphs
earlier in this guide is correct.
(cherry picked from commit 3074fdb3ab)
Five redirects sent people to the home page instead of a guide, because
their target no longer exists: the four `guide/ngmodules/*` entries point
at `/guide/ngmodules`, which has no route, and
`guide/animations/transitions-and-triggers` had a plural in a target that
is registered as `transition-and-triggers`.
Two more resolved only after a second hop, redirecting to a path that is
itself a redirect.
The spec only checked that a `redirectTo` starts with a slash, which all
seven satisfied, so it now also checks that the target is a real page and
that it is not another redirect.
(cherry picked from commit 39da06138b)
`stableTypeOrdering` was recently enabled in the internal builds. While there aren't any breakages in Angular, these changes enable it so we can catch potential issues earlier.
(cherry picked from commit c18261f3fd)
The regular expressions in `_scopeAnimationRule` expect an `animation` or `animation-name` property to be preceded by whitespace or a semicolon, and its value to end at a semicolon. Minified CSS breaks both assumptions. Inside an at-rule the property follows a `{`, and the last declaration of a block has no trailing semicolon, so the closing `}` lands inside the captured value. The keyframe name is then left unscoped while the `@keyframes` rule itself is renamed, so the animation does nothing in a production build.
Accept `{` as a leading boundary and stop the value at `}`. The prefix is written back unchanged, and a declaration value cannot contain an unescaped `}`.
Fixes#70316
(cherry picked from commit 58b0cb4735)
The readonly rule previously used 'when' in configOrLogic to determine if a configuration object was provided. Under property renaming/minification, the string literal property lookup fails and causes the rule to fall back to being permanently readonly.
This change switches to the dot-access form (configOrLogic?.when), matching hidden() and ensuring property renaming works correctly.
(cherry picked from commit 1e35de536d)
Currently the standalone migration only copies symbols referenced from the NgModule metadata into the main file. Top-level `registerLocaleData` calls, and the default imports they depend on, are dropped silently when the module file is pruned, which breaks locale resolution at runtime with a "Missing locale data" error.
Copies top-level `registerLocaleData` calls from the file of the bootstrapped module into the main file, and adds support for carrying over default imports which were previously skipped silently by the reference resolution.
Fixes#50886
(cherry picked from commit bdc09e8183)
The quick fix and completion auto-import always generated the module specifier with double quotes, ignoring the user's quote preference and the style used in the file.
The generated import now respects the `quotePreference` from the TypeScript user preferences. When the preference is `auto` (or absent), the style is inferred from the first existing import in the file, mirroring TypeScript's own behavior. Files with no imports
keep the previous double-quote default.
Fixes#67108
(cherry picked from commit 0f0d52e9c1)
The `embeddable` glob excludes nine example directories. Four of them,
`testing`, `ssr`, `resolution-modifiers` and `dependency-injection`, were
removed by #66753 and #61686 without updating this file, so those patterns
match nothing.
Three of the four sit under a TODO about examples that do not compile. Those
were deleted rather than fixed, which leaves `reactive-forms` and
`form-validation` as the only two the note still applies to.
(cherry picked from commit c6a6ad0a44)
Use the Subscription type returned by Observable.subscribe instead of storing async validator subscriptions as any.
(cherry picked from commit a4a428ece2)
`llms-list.md` feeds the generated `llms-full.txt`. Three of its DI
entries were left behind by a page rename: two named files that no longer
exist and one named a copy that had been superseded. The generator warns
on an unreadable file and carries on, so the build stayed green while
`llms-full.txt` quietly lost three DI guides and shipped a stale fourth.
`llms.txt` linked the same old URLs, which only reach their pages through
a redirect, plus `guide/hybrid-rendering`, which redirects to `/guide/ssr`
and duplicates the line above it.
Also removes `creating-injectable-service.md`, left behind by that rename
and reachable from nothing.
(cherry picked from commit 51cb07e980)
The new home page in #63662 stopped rendering `HomeAnimationComponent`
and its directory was left behind. The animation that runs today lives
in `features/home/animation`, which replaced it.
Nothing references the class, its `adev-home-animation` selector, or any
file in the directory, and no stylesheet or build target pulls it in.
(cherry picked from commit eb6570b06d)
`getApiNavigationItems` cast the manifest to `any` behind a TODO waiting
on #66252, which was closed without merging, so the three `any`s it
guarded were never going away on their own.
The real `ApiManifestPackage` cannot be imported here: navigation
entries are built as a standalone `ts_project` so the route generator
can consume them at build time, and it has no dependency on the app
tree. Declaring the handful of fields this file reads gets rid of the
`any`s without touching the build graph.
(cherry picked from commit 4997521685)
`DiagnosticsState` carried a TODO to move off `BehaviorSubject` once
zoneless was turned on. adev has been zoneless for a while now, with no
zone.js dependency and an empty `polyfills` array, so the condition is
met and the TODO can go.
Both consumers now derive from the signal instead of subscribing. The
Console tab badge becomes a `computed`, and so does the code editor's
error list: the diagnostics are produced by a `linter()` configured with
a 400ms delay rather than arriving as a stream, so the rxjs chain was
only adding a further second before they were displayed. The errors box
stays dismissable through a `linkedSignal`, which reverts as soon as the
diagnostics change.
(cherry picked from commit 238d8bf98a)
The angular.dev origin was declared twice as a local `ANGULAR_DEV`
const and hardcoded inline in two more places. It now lives once in
`core/constants/links.ts`, next to `ANGULAR_LINKS`.
(cherry picked from commit 593f2d7e2e)