When `rootDir` was set in a project's tsconfig (e.g. `rootDir: "src"`),
tsurge-based migrations would fail because `projectRoot` was derived from
`rootDir`, causing `rootRelativePath` to be computed relative to `src/`
instead of the workspace root. This produced paths like `app/app.ts`
instead of `src/app/app.ts`, which the DevKit tree could not resolve.
Fix by overriding `info.projectRoot` to `absoluteFrom(info.program.getCurrentDirectory())`
immediately after program creation, ensuring workspace-relative paths are
used for all tree updates.
When partial declarations are not preprocessed to AOT by the linker, the `ngDeclareComponent`
call causes them to be compiled ad-hoc. In this mode, NgModule imports in standalone components
would be dropped, deviating from the linker. This commit changes the ad-hoc compilation of
component declarations to pass the NgModule imports along just like the linker does.
Fixes#69451
Initialize `zoneSymbolEventNames` and `patches` with `Object.create(null)` instead of `{}`.
This is a hardening change rather than a fix for an exploitable vulnerability. Calling `addEventListener('__proto__', fn)` is not directly attacker-controlled; its presence already implies an application bug. However, if such a call does occur, the current implementation can behave unexpectedly depending on the environment.
For `zoneSymbolEventNames`, accessing `zoneSymbolEventNames['__proto__']` on a plain object invokes the inherited `__proto__` accessor and returns `Object.prototype`, which is truthy. This causes `prepareEventNames()` to be skipped, leaving `symbolEventName` undefined and eventually leading to a runtime error when `window['undefined'] = []` is executed.
In Node.js environments running with `--disable-proto=throw`, the assignment:
```ts id="z8n4qm"
zoneSymbolEventNames['__proto__'] = {};
```
throws immediately because it triggers the disabled `__proto__` setter.
The `patches` registry has a similar issue. A `__proto__` key passed to `__load_patch()` bypasses the duplicate-patch check and reaches:
```ts id="f3v7kx"
patches['__proto__'] = fn(...);
```
which invokes the `__proto__` setter and changes the prototype of the `patches` object.
Using `Object.create(null)` removes the inherited `__proto__` accessor entirely, causing these keys to behave like ordinary properties rather than interacting with JavaScript's prototype machinery.
As part of this change, `patches.hasOwnProperty(name)` is also updated to:
```ts id="n2c8wp"
Object.prototype.hasOwnProperty.call(patches, name)
```
since null-prototype objects do not inherit `hasOwnProperty`.
`model()` signals are special because they combine a signal input with a writable output through an internal `OutputEmitterRef`. During upgrade, `setupOutputs()` subscribes to that emitter to keep Angular → AngularJS two-way binding working.
The issue was that `updateInput()` could overwrite the signal property directly when `isSignal` was `false` (which happens in JIT mode and when `unsafelyOverwriteSignalInputs` is enabled). Once that happened, the original `OutputEmitterRef` was lost, so the two-way binding stopped working.
The fix detects `model()` signals at runtime by checking for both `[SIGNAL]` and a writable `.set()` method, which distinguishes them from read-only `input()` signals. When those traits are present, updates are always applied through `applyValueToInputSignal()` instead of replacing the property directly, regardless of the `unsafelyOverwriteSignalInputs` setting.
Fixes#60599
Without this change, components that use RouterLinkActive in multiple
contexts (e.g. both a navigation menu and body content) are forced to
branch the template for every conditional input:
@if (activeClass) {
<a [routerLink]="href" [routerLinkActive]="activeClass"
[routerLinkActiveOptions]="activeOptions"
[ariaCurrentWhenActive]="ariaCurrent">
<ng-content />
</a>
} @else {
<a [routerLink]="href"><ng-content /></a>
}
Every additional input multiplies the branching, and each @if/@else
injects unwanted comment nodes into the DOM. There is no way to
conditionally attach a directive in Angular templates, making imperative
TypeScript instantiation the only alternative.
Accepting null/undefined collapses this to a single template branch:
<a [routerLink]="href"
[routerLinkActive]="activeClass"
[routerLinkActiveOptions]="activeOptions"
[ariaCurrentWhenActive]="ariaCurrent">
<ng-content />
</a>
When activeClass is undefined (e.g. in content areas), the directive
stays mounted but applies no CSS classes. When it is a string (e.g. in
the navigation), normal active-class behavior applies — no branching, no
extra DOM nodes, no TypeScript workarounds.
- `routerLinkActive`: null/undefined now sets an empty class list.
- `routerLinkActiveOptions`: null and undefined are treated differently:
- undefined → falls back to the default subset match ("not set")
- null → explicit opt-out, link is never considered active
Closes#66233
Preserve explicit referrer policy when the service worker reconstructs asset requests for cache-busted and redirected asset fetches.
For example, an application can load a script or image with referrerPolicy: 'same-origin' or 'origin' to limit referrer data. Dropping that policy can expose more of the current URL to that resource host.
Preserve referrer metadata when the service worker reconstructs asset requests for cache-busted and redirected asset fetches.
For example, an attacker with access to asset host logs could receive a reset token embedded in a page URL if the reconstructed request falls back to default referrer behavior instead of carrying referrer: ''.
`parseHostBindings` throws plain `Error`s for malformed host bindings
(e.g. a property binding with a non-static value, as can happen while
editing in the language service). These were uncaught during directive
analysis, crashing the compiler and the Angular Language Service.
Wrap the call and surface the error as a `FatalDiagnosticError` so it
becomes a diagnostic and analysis can complete normally.
Fixes#69106
A custom FormValueControl only participates in debounce('blur') if it emits
the touch output on the native blur event. This was undocumented, and the
touch name reads like a focus event, so users wired it to (focus) and
blur-based debouncing silently did nothing.
Add a dedicated guide section with a working example, link the debounce API
reference to it, and clarify the touch JSDoc that it must fire on blur, not
focus.
Fixes#69370
Explain that MIN and MAX are selection keys which point to the type-specific limit metadata keys, such as MIN_NUMBER, MIN_DATE, MAX_NUMBER, and MAX_DATE.
Also list minDate() and maxDate() alongside min() and max() in the Signal Forms metadata docs, so the validator tables match the actual metadata model.
Adds a 'Chaining resources' section to the resource guide covering:
- Basic usage of chain() to depend one resource on another
- Status propagation for all ResourceStatus values (idle, loading,
reloading, error, resolved, local)
- Chaining vs reading .value() directly, shown as an avoid example
- Guidance on passing the chained value directly as params
Also adds an @see link from ResourceParamsContext to the new section.
Closes#69329
Adds an error reference page for NG05102 (UNSUPPORTED_EVENT_TARGET) explaining
what triggers it and how to fix it. Also marks the error code as negative (-5102)
so that in dev mode the error message automatically links to the new guide page
on angular.dev/errors, consistent with other documented runtime errors.
Skip HttpTransferCache serialization for HTTP responses that contain a
Set-Cookie header.
Cookie-setting responses commonly represent session-specific,
user-specific, or security-sensitive state. Serializing their bodies into
SSR TransferState can embed sensitive data into the generated HTML, where
it may be reused during hydration or replayed by a shared cache/CDN.
- Rename `DirectiveForestHooks` to `DirectiveForestManager`
- Keep profiler reference standalone; move it out from `DirectiveForestHooks`/`DirectiveForestManager`
- Convert profiler-specific `IdentityTracker` behavior to a more generalized one
- Separate timing API functionality from the `DirectiveForestHooks`/`DirectiveForestManager` initialization fn
- Reorganize files; rename `/hooks` to `/profiling`
- Use concrete types for directive and component instances (incomplete coverage; based on `any` at the moment)
- Other more minor changes
The "Community translations" column heading wrapped to two lines while
the other footer headings stayed on one. Add a `white-space: nowrap`
rule on the footer headings, scoped to the wide (4-column) layout. The
breakpoint is driven by a shared `$columns-breakpoint` variable so the
nowrap rule and the grid's 2-column collapse stay in sync.
Update the security guide to document that the Angular SSR request handling pipeline now supports and validates the standard RFC 7239 `Forwarded` header.
Previously, only non-standard `X-Forwarded-*` headers were supported for resolving proxy-forwarded protocols, hosts, and ports. With this update:
- The standard `Forwarded` header parameters (such as `host` and `proto`) are validated and take precedence over corresponding `X-Forwarded-*` headers when trusted.
- The `trustProxyHeaders` option and `NG_TRUST_PROXY_HEADERS` environment variable can be configured to trust the `Forwarded` header.
For more details on the implementation, see the corresponding PR:
https://github.com/angular/angular-cli/pull/33406
Fixes a minor issue where the `preload` function in `injectAsync` might cause an uncaught promise error.
I also fixed that in `onIdle` we were passing the wrong function into `assertInInjectionContext`.
The bottom CTA heading on the landing page read "Want to learn more about
Angular ?" with a space before the question mark, while the matching heading
higher up the page correctly had no space. Removed the space so both read
"Want to learn more about Angular?".
Adds a new error reference page for NG05101 (NO_PLUGIN_FOR_EVENT),
which is thrown when no registered EventManagerPlugin supports the
event name passed to addEventListener. The page covers the two common
causes: a typo in the event binding and a missing plugin provider.
The `@Component` inline-code span was directly adjacent to the
`[decorator](...)` link, rendering as a mashed-together token in the
source. Added a space so it reads "A `@Component` [decorator] that..."
as intended.
After #68581 graduated the Signal Forms APIs to public API and #68654 removed
the experimental warnings from the Signal Forms documentation in adev, the
package READMEs still framed the API as experimental.
Update `packages/forms/signals/PACKAGE.md`: drop the experimental title and
intro, remove the now-shipped entries from "Not yet supported" (interop with
reactive/template forms and strongly-typed binding to UI controls), and remove
the remaining experimental and exploratory wording from the FAQ.
Update `packages/forms/PACKAGE.md`: it listed only two ways to build forms
(reactive and template-driven). Add signal forms as the third.
Fixes#68724
The code tabs rendered by the example viewer (e.g. the npm/pnpm/yarn/bun
install tabs) paint their active label as transparent text clipped to a
gradient. Material's MDC tab styles add `transition: color 0.15s linear`
to `.mdc-tab__text-label`, plus a 100ms delay on the active tab. Because
that transition animates `color` from the solid label color to
transparent, the solid color stays visible on top of the gradient for
~100ms when a tab is activated, which reads as a white flash.
Disable the transition on these labels so the color switches instantly,
and target `.mdc-tab__text-label` directly (instead of a generic `span`)
so `color: transparent` drives the gradient clip cleanly.
When a documentation table has a wide content column, the narrow header
columns get squeezed and inline-code header labels break mid-word at
their hyphens. On the MCP server tools page this rendered the `local-only`
and `read-only` column headers as `local-` / `only` and `read-` / `only`.
Apply `white-space: nowrap` to `code` inside `th` so header tokens stay on
a single line. The rule is scoped to headers, whose labels are always
short, so long code signatures in body cells continue to wrap and no
table gains a horizontal scrollbar.
The rules_angular bump in #69410 made ts_project require every `deps`
entry to provide the JsInfo provider. `esbuild_base` listed
`//packages:package_json` in its deps, but `esbuild-base.config.mts` never
imports or reads package.json, so the dependency is unnecessary and now
breaks analysis (`//packages:package_json` is a `copy_to_bin` target that
provides only DefaultInfo). Remove it.
The cross-repo dependency update in #69410 bumped rules_angular, whose
ts_project now requires every entry in `deps` to provide the JsInfo
provider. Two adev targets passed deps that don't, so `bazel build
//adev:build` fails analysis and the adev CI check has been red on main
since that PR.
Make generate_nav_items return JsInfo (with the generated routes.json as
its sources) so navigation-entries can keep importing routes.json through
its deps. Also drop the spurious deps entry on llms_lib: llms.mts reads
llms-list.md at runtime via readFile rather than importing it, and the
file is already provided to the binary via data.
Fixes#69429
PR angular/components#33364 restored the `readonly` property for the
combobox, but the documentation was not updated to reflect this change.
This commit adds the missing `readonly` input and its description to
the Inputs / Model table in the combobox guide.
The page title row (`.docs-page-title`) relied on the default flex
alignment, so the edit icon next to the title did not line up with the
title text. Add `align-items: baseline` so the icon sits on the title's
baseline.