Compare commits

...

303 Commits

Author SHA1 Message Date
Alon Mishne dca0a77488 release: cut the v21.2.20 release 2026-08-12 17:02:31 -07:00
Jaime Burgos caf616670f fix(http): run root interceptors in the terminal request chain
Represent withRequestsMadeViaParent() with an internal delegating backend so the interceptor handler can distinguish delegated clients from independent child configurations.
2026-07-31 15:13:30 -07:00
SkyZeroZx fec5977df4 fix(http): match header values exactly when deleting
Normalize value-specific HttpHeaders deletions before filtering. The string overload previously used String#indexOf and removed shorter values contained within the requested deletion value, potentially widening outgoing request metadata.

Preserve delete-all behavior only when no value is supplied, and cover string, array, and empty-string deletion.
2026-07-31 15:13:30 -07:00
SkyZeroZx e33d69a71c fix(http): preserve immutability of materialized clones
Prevent lazy HttpHeaders and HttpParams clones from reusing value arrays owned by a materialized source. Append and value-specific delete operations previously mutated those shared arrays, violating the immutable API contract and allowing request metadata to bleed into later requests.

Share value arrays until an update mutates a specific header or parameter, then copy only that array. Cover the affected append and delete paths with regression tests that materialize the source first.
2026-07-31 15:13:30 -07:00
Jaime Burgos 6afe6fa781 fix(core): sanitize host bindings on concrete hosts
Compute host binding security contexts against concrete hosts, including host directives, inheritance, dynamic directives, and createComponent hostElement usage.
2026-07-30 08:29:04 -07:00
Alan Agius 07cb840eac release: cut the v21.2.19 release 2026-07-29 22:44:48 +02:00
Alan Agius 7b884f585a fix(compiler): restrict possible event handler check to property names longer than 2 characters
Previously, the compiler disallowed translation of any attribute starting with 'on' for security reasons. This incorrectly disallowed translation of the 'on' attribute itself, which is not an event handler.

This commit introduces `isPossibleEventHandler` to verify that the property name has a length greater than 2 in addition to starting with 'on'. This allows attributes like 'on' to be translated while still correctly disallowing actual event handlers like 'onerror', 'onclick', etc.
2026-07-29 08:51:50 -07:00
Hexix23 948a8d6831 fix(http): distinguish repeated transfer cache params
Serialize transfer cache request parameters without comma-joining repeated values so distinct HttpClient requests cannot reuse the same cached response.
2026-07-29 08:51:50 -07:00
Hexix23 e2660c3dee fix(compiler): disallow i18n event attributes
Reject translated event-handler attributes so localization cannot bypass Angular event-attribute validation.
2026-07-29 08:51:50 -07:00
SkyZeroZx f34a93c946 fix(platform-server): update domino to latest version
Updates the domino dependency to the latest version as used in the main branch.

This update contains fixes for https://github.com/angular/domino/pull/32.
2026-07-29 08:43:40 -07:00
Jaime Burgos 9949dccce1 fix(http): enable xsrf for root-provided HttpClient
Include the XSRF interceptor in the root token factory so the automatically provided HttpClient retains the documented default protection without requiring provideHttpClient().
2026-07-24 13:24:13 -07:00
leonsenft 5d00aa2bd4 release: cut the v21.2.18 release 2026-07-08 15:02:15 -07:00
SkyZeroZx e86c31bf26 fix(service-worker): preserve referrer policy in asset requests
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.

(cherry picked from commit a7f52e5c30)
2026-07-08 11:24:32 -07:00
SkyZeroZx 1804f73bec fix(service-worker): preserve referrer in asset requests
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: ''.

(cherry picked from commit 99ad47e58f)
2026-07-08 11:24:32 -07:00
SkyZeroZx 91df739b80 fix(http): prevent caching of responses with Set-Cookie headers
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.

(cherry picked from commit 80795defc6)
2026-07-07 10:15:02 -07:00
Alan Agius 8d22cc953b fix(compiler-cli): update babel dependencies to latest v7
Update babel dependencies to v7.29.7 to address CVE-2026-49356.

Fixes #69608
2026-07-06 14:01:46 -07:00
SkyZeroZx 5a693bafcd fix(core): reject dynamic script host elements
The previous fix for GHSA-692r-grfm-v8x7 was incomplete because it rejected script tags only when locating an explicit host element. Dynamic component instantiation can also infer the host element from the component selector.

Move the script-host rejection to the point where ComponentFactory has resolved the host element for either path, so createComponent rejects script hosts consistently.

(cherry picked from commit 135f3755b4)
2026-06-30 17:42:39 -07:00
SkyZeroZx 6bcce117fb fix(core): avoid caching missing locale data
Only cache locale data loaded from the global locale registry when an actual locale entry is found.

This prevents attacker-controlled missing locale identifiers from being retained indefinitely in SSR when locale lookup falls back to a parent locale or the built-in English locale, avoiding unbounded process memory growth in locale-aware pipes and formatters.

(cherry picked from commit ea8277ae37)
2026-06-24 13:04:05 -04:00
Andrew Scott 31c25e2e85 release: cut the v21.2.17 release 2026-06-10 12:54:23 -07:00
Jaime Burgos 88832c84f8 fix(core): validate lowercase SVG animation attribute names (#69269)
Normalize SVG animation attributeName lookup to also recognize lowercase attributename before allowing dynamic animation value bindings.

Add runtime and platform-server SSR regression coverage for lowercase attributename retargeting.

(cherry picked from commit d5e689af80)
2026-06-10 09:51:11 -07:00
Alan Agius 13fb0afe93 refactor(platform-server): deprecate ServerXhr (#69255)
XHR support in `@angular/platform-server` is deprecated because the underlying `xhr2` library does not safely handle redirects. Specifically, it can forward `Authorization` headers on cross-origin redirects (which leaks credentials) and is susceptible to denial-of-service (DoS) via redirect loops.

DEPRECATED: XHR support in `@angular/platform-server` is deprecated. Use standard `fetch` APIs instead.
2026-06-09 10:06:45 -07:00
SkyZeroZx 86a56dc279 fix(common): Limits date format string length
Introduces a maximum length of 256 characters for date format strings.

This prevents potential Denial of Service (DoS) attacks by throwing an
`INVALID_DATE_FORMAT` error if an excessively long format string is
provided to `formatDate` or `DatePipe`, safeguarding against performance
degradation or application crashes.

(cherry picked from commit 35de6b368c)
2026-06-08 13:19:55 -07:00
SkyZeroZx bcb1b7ea25 fix(http): preserve empty referrer option in HttpRequest
Preserve `referrer: ''` when constructing and cloning HttpRequest.

An empty string is a valid Fetch referrer value and is documented by
Angular as the way to omit referrer information for sensitive requests.
The previous truthy checks treated it as if the option was not provided,
causing requests to fall back to the browser default referrer behavior.

(cherry picked from commit 1e54b8f5ec)
2026-06-08 13:17:32 -07:00
SkyZeroZx b9d29381bb fix(service-worker): Strips sensitive headers on cross-origin redirects
Removes `Authorization`, `Cookie`, and `Proxy-Authorization` headers when a request is redirected to a different origin. This aligns with the Fetch API's redirect algorithm to prevent sensitive information from being sent to third-party origins.

(cherry picked from commit 423a109404)
2026-06-08 10:41:59 -07:00
Jaime Burgos a810a319d1 fix(http): Rejects non-HTTP(S) URLs in JSONP requests
Prevents JSONP requests from using URLs with unsupported protocols
for improved security.
2026-06-05 14:25:46 -07:00
Alan Agius 35510746b7 fix(platform-server): harden platform location origin validation during SSR
Add allowOriginChange option to ResolveUrlOptions in resolveUrl to enforce same-origin validation on resolved URLs. When set to false, it prevents any cross-origin changes (including HTTP/HTTPS URLs), aligning the emulated server-side platform location environment with browser security behavior.

Refactor ServerPlatformLocation.replaceState to use allowOriginChange: false instead of manual comparison, hardening state change validation against cross-origin URLs.

Add unit tests in url_spec.ts and platform_location_spec.ts for the origin validation changes.
2026-06-05 10:46:12 -07:00
Alan Agius a5f82b8315 refactor(platform-server): replace standard Error with RuntimeError
Update platform-server to use Angular 's native `RuntimeError` class.
This aligns error throwing patterns in platform-server with other packages of the framework such as core, common, and platform-browser.

For URL and host errors, the error messages are configured to return only the raw dynamic URL when `ngDevMode` is false (in production) to aid in troubleshooting without bloating production bundles.
2026-06-05 10:46:12 -07:00
Alan Agius bc55749698 fix(common): use cryptographically secure SHA-256 for transfer cache key generation
Replace the custom 64-bit non-cryptographic combined DJB2 hashing implementation in HttpTransferCache with a robust, pure JavaScript, synchronous SHA-256 algorithm.

Using DJB2 is vulnerable to pre-image and second-preimage attacks due to its small 64-bit keyspace and mathematical simplicity. An attacker could craft colliding request inputs to poison the cache, potentially causing a CDN or the application to serve the wrong cached response to legitimate users.

SHA-256 provides strong cryptographic collision resistance, preventing cache key collision attacks. A custom synchronous implementation is required because the Web Crypto API (`crypto.subtle.digest`) is asynchronous, whereas the transfer cache state lookup and interceptor flow must operate synchronously.

Also, update the unit tests to dynamically verify the custom SHA-256 output against the native Web Crypto API.
2026-06-05 09:47:53 -07:00
SkyZeroZx d846326b07 fix(common): skip transfer cache for uncacheable HTTP traffic
Do not store HTTP transfer cache entries when either the request or response uses `Cache-Control: no-store`, `Cache-Control: private`, or `Cache-Control: no-cache`.

Also skip transfer cache when requests use the Fetch API `cache` option with `no-store` or `no-cache`.

Because transfer cache serializes SSR HTTP responses into the rendered HTML, Angular now treats these directives conservatively to avoid exposing sensitive or explicitly uncacheable data through `TransferState`.

(cherry picked from commit 4d150156ca)

(cherry picked from commit 64ce11fcd4)
2026-06-04 15:26:47 -07:00
SkyZeroZx e245d40c4d fix(http): skip transfer cache for fetch credentialed requests
Treat HttpClient requests using `credentials: 'include'` and `same-origin` as credentialed when deciding whether a response can be stored in the HTTP transfer cache.

The transfer cache already skips requests with `withCredentials`, `Cookie`, `Authorization`, or `Proxy-Authorization` because those responses may contain user-specific data. Fetch-backed requests can express the same credentialed behavior through the `credentials` option, so these responses must not be serialized into the SSR HTML.

This keeps credentialed SSR responses out of TransferState and aligns the cache eligibility check with the fetch request options supported by HttpClient.

(cherry picked from commit 8ec01970d2)
2026-06-04 15:26:47 -07:00
SkyZeroZx dc9c99636d fix(compiler): sanitize two-way properties
Apply schema-derived sanitizer resolution to TwoWayProperty ops so native two-way DOM bindings emit the same sanitizer as one-way property bindings.

Add compiler compliance coverage for innerHTML, srcdoc, URL, resource URL, and security-sensitive attribute cases.

(cherry picked from commit 9ca51ab0e4)
2026-06-03 13:06:27 -07:00
Matthieu Riegler 1523061137 fix(core): harden TransferState restoration against DOM clobbering
Reject non-script elements when reading the SSR transfer state payload by id.
This prevents attacker-controlled elements with a clobbered id from spoofing
hydration state.
2026-06-03 12:25:38 -07:00
Pawel Kozlowski 03188ddc9f release: cut the v21.2.16 release 2026-06-03 14:29:59 +02:00
Matthieu Riegler 736c4ab7e6 refactor(core): fix broken unit test.
This was broken by 3fd6897
2026-06-02 14:17:05 +02:00
Matthieu Riegler 3fd6897a67 fix(core): harden inherit definition feature against polluted prototypes
Stop inheritance traversal before built-in prototype objects and only read `ɵcmp`/`ɵdir` when they are own properties of a super type. This prevents polluted inherited properties from being treated as Angular defs during inheritance merging.

Also adds regression tests covering polluted `Object.prototype.ɵdir` and `Object.prototype.ɵcmp` to ensure polluted host metadata is not inherited.

(cherry picked from commit e695379354)
2026-06-02 13:26:00 +02:00
Michael Small db157e4aff docs: fix min/max form template examples
(cherry picked from commit 42391329c2)
2026-06-02 11:23:07 +02:00
Joey Perrott 70af5e8abd fix(docs-infra): secure update-assets script against RCE and SSRF
- Validate storedSha and storedBranch from _build-info.json.
- Validate latestSha returned from GitHub API.
- Validate branch in GithubClient.getShaForBranch and baseSha/headSha in GithubClient.getAffectedFiles.
- Use execFileSync instead of execSync to avoid shell execution.

TAG=agy
CONV=4e3e69ba-3f3d-416b-9ce4-9ef75486d2f3

(cherry picked from commit 3093edcad0)
2026-06-02 11:22:02 +02:00
Alan Agius 66821c4ed5 fix(platform-server): throw on suspicious URLs and restrict protocol-relative URLs
Currently, the platform-server attempts to neutralize URL hijacking and SSRF
bypasses by collapsing multiple leading slashes in relative paths. However,
sophisticated bypasses using obfuscated protocols (e.g., carriage returns or
newlines) or relative-like backslash paths can still lead to unexpected
origin takeovers.

This commit improves security by doing the following:
- Rejects protocol-relative URLs by throwing an error if they are not
  explicitly permitted via `allowProtocolRelative`.
- Strictly validates resolved URLs against the provided origin using
  `isSafeOriginChange`. If a URL unexpectedly shifts origins, an error
  is thrown.
- Permits origin changes only when standard absolute http/https protocols are
  explicitly declared in the input URL.

(cherry picked from commit 0b19c92d44)
2026-06-01 20:03:28 +02:00
Matthieu Riegler b74fb76d1a docs(docs-infra): simplify code block styles
#68940 introduced a regression that broke style for wrapped `code` blocks.
Simplifying the style by droping the unecessary gradient + before workaround fixes the issue.

(cherry picked from commit ec4f08bb94)
2026-06-01 18:36:58 +02:00
KirtiRamchandani 66d09558b6 docs: mention Angular Aria in accessibility guide
(cherry picked from commit 9b5952e3a3)
2026-06-01 18:33:36 +02:00
Kristiyan Kostadinov ae1c8a1f7a fix(compiler): move projection attributes into constants
We can save some memory by moving the `attrs` passed into the `projection` instruction into the constant pool.

(cherry picked from commit f0b28f6443)
2026-06-01 12:28:34 +02:00
Kam 9f6fda6815 docs: fix esbuild and TensorFlow branding on overview page
The esbuild card on the "What is Angular?" page rendered the bundler name three different ways (title "esbuild", link "ESBuild and Vite", body "Vite and ESBuild") so this unifies on the official lowercase "esbuild"; also corrects "Tensorflow" to "TensorFlow" to match the other brands on its line (Firebase, Material Design, Flutter, Google Cloud) which were already cased correctly.

(cherry picked from commit f777dd112e)
2026-06-01 12:19:08 +02:00
arturovt 1e5d76bfd9 docs: document //i18n(ph="name") placeholder syntax for template interpolations
The `//i18n(ph="name")` comment syntax for naming interpolation placeholders
in templates was not documented anywhere in the i18n guide, despite being a
supported compiler feature with test coverage.

Add a "Name the interpolation placeholder" subsection under "Mark text in
component template" in prepare.md, mirroring the existing subsection in
"Mark text in component code". Includes a cross-reference to the $localize
equivalent (`${var}:name:`) to help readers connect the two approaches.

Closes #52070

(cherry picked from commit 2b44a07ea7)
2026-06-01 12:18:09 +02:00
aparziale 22dd53ca97 docs: fix link debbuging and extended-diagnostics
Update link for 'Improve debbuging with better Angular error messages' and 'extended-diagnostics' sections

Fixed #69010

(cherry picked from commit cbc36f59e0)
2026-06-01 12:17:03 +02:00
Kam 1cd4f54aef fix(docs-infra): readable contrast for DEV/EXP api badges in light mode
The DEV (developer preview) and EXP (experimental) badges in the API reference list used `--page-background` for text, which is dark in dark mode (working as intended on the pale colored bg) but white in light mode, making the labels invisible against the near-white badge bg. Introduce an `--item-attr-text` CSS variable defaulting to `--page-background` and overridden to `--primary-contrast` in light mode, following the per-mode pattern the file already uses for `--item-attr-base-mix`.

(cherry picked from commit 0010ad5910)
2026-06-01 11:57:54 +02:00
Bhuvansh855 9d8ea2cc9a docs(forms): remove hasMetadata references from v21 guide 2026-06-01 11:56:00 +02:00
Kristiyan Kostadinov 69c0d48a0d fix(docs-infra): round up media queries
Uses 1px increments for media queries, rather than the 0.01px we have now which seem to be a bit too precise and cause the UI to be stuck between states in some cases.

I've also removed some unnecessary `calc`, because the calculation is happening inside Sass already.

Fixes #69020.

(cherry picked from commit 96ed0fe45b)
2026-06-01 11:34:44 +02:00
arturovt 7e38336dc7 fix(core): use Object.create(null) for LOCALE_DATA as a hardening measure
Prior to this commit, `LOCALE_DATA` was initialized as a plain object literal:

```typescript
let LOCALE_DATA: {[localeId: string]: any} = {};
```

While `__proto__` is neutralized by the `replace(/_/g, '-')` sanitization step (becoming `--proto--`), keys like `constructor` and `prototype` pass through unchanged and would modify special properties on `Object.prototype` if used as bracket notation keys on a plain object.

**Example attack through the public API:**

```typescript
// attacker calls the public registerLocaleData API with a crafted localeId
registerLocaleData(data, 'constructor');

// internally becomes:
LOCALE_DATA['constructor'] = data;
// → modifies Object.prototype.constructor for every object in the process

// or with extraData:
registerLocaleData(data, 'constructor', extraData);
// LOCALE_DATA['constructor'][LocaleDataIndex.ExtraData] = extraData;
// → Object.prototype[LocaleDataIndex.ExtraData] = extraData
// → every plain object in the process now has this property
// → affects JSON serialization, property enumeration, and framework internals

// consequence — any subsequent object created in the process is affected:
const user = getUserFromSession();
console.log(user[LocaleDataIndex.ExtraData]); // → attacker-controlled value
```

In a long-running SSR server this pollution persists for the lifetime of the process and affects all subsequent requests from all users.

**The fix** initializes `LOCALE_DATA` with `Object.create(null)`:

```typescript
let LOCALE_DATA: {[localeId: string]: any} = Object.create(null);
```

A null-prototype object has no prototype chain, so any key is treated as a plain string with no special behavior, making prototype pollution impossible regardless of input — without relying on the sanitization step as the sole protection.

(cherry picked from commit 0deac976f3)
2026-05-29 14:55:53 +02:00
arturovt 34c4e401ba fix(zone.js): validate __Zone_symbol_prefix to prevent DOM clobbering attacks
Previously, `__Zone_symbol_prefix` was read directly from `globalThis` without validating its type:

const symbolPrefix = global['__Zone_symbol_prefix'] || '__zone_symbol__';

This made it possible for DOM clobbering to interfere with Zone’s internal symbol handling. If an attacker injected a DOM element with the same name (for example via a form field or anchor ID), `global['__Zone_symbol_prefix']` could resolve to a DOM element instead of a string. Because DOM elements are truthy, the fallback would not be used, and Zone would construct invalid internal keys (e.g. “[object HTMLFormElement]...”), breaking patching and lookup logic in subtle ways.

This prevents DOM clobbering from influencing Zone’s internal symbol generation and keeps the patching system stable even in the presence of malicious or unexpected global values.

(cherry picked from commit e50f504b2f)
2026-05-29 14:54:19 +02:00
rootvector2 f6d8e642b0 fix(common): only strip a literal /index.html suffix from URLs
Hit this while exercising `Location.normalize` with route paths that end in non-`.html` suffixes.

The unescaped `.` in the strip regex inside `_stripIndexHtml` matches any character, so e.g. `/foo/indexXhtml` and `/foo/index_html` both collapse to `/foo` before the base-path strip and end up resolving to the wrong route.

Escape the dot so only the literal `/index.html` suffix is stripped.

(cherry picked from commit d109bf90d5)
2026-05-29 13:16:08 +02:00
Alan Agius 8206972189 refactor(platform-server): clean up and simplify url resolution utility
Trims leading/trailing whitespaces in resolveUrl to normalize input.

(cherry picked from commit e14d34e9ee)
2026-05-29 13:14:13 +02:00
Alan Agius d3170031b6 fix(platform-server): update domino to latest version
Updates the domino dependency to the latest version as used in the main branch.

This update contains fixes for https://github.com/angular/domino/pull/29.
2026-05-29 13:12:01 +02:00
arturovt 9b7d0e5034 docs: document i18n object forms for sourceLocale and locales in angular.json
The `sourceLocale` and `locales` entries in `angular.json` accept object
forms (with `code`, `baseHref`, and `subPath`) that were never documented.

- Add an `i18n options` reference section to workspace-config.md covering
  the full shape of `sourceLocale` and each `locales` entry, including the
  distinction between `baseHref` (HTML only) and `subPath` (HTML + output
  directory name)
- Add `i18n` to the project configuration options table in workspace-config.md
- Expand the suboptions table in merge.md to mention the object forms and
  link to the new reference section

Closes #59664

(cherry picked from commit 2f49d5dba4)
2026-05-29 11:53:54 +02:00
Alex Rickabaugh cea6588bb3 release: cut the v21.2.15 release 2026-05-28 09:51:14 -07:00
Kam 6b8202eab6 refactor(docs-infra): extract magic 27 in navigation-list tooltip threshold
The matTooltip on navigation list items was disabled when the label was
shorter than the literal `27`, repeated across four bindings in the
template. Lift the value to a protected readonly field so the threshold
has a name and lives in one place.

(cherry picked from commit 34d577f697)
2026-05-28 16:08:16 +02:00
Alan Agius eb1cbbf2eb fix(compiler): prevent namespaced SVG <style> elements from being stripped
Updates the template preparser to exclude namespaced SVG style tags (':svg:style') from the style elements set.

Previously, ':svg:style' elements were incorrectly classified as PreparsedElementType.STYLE, which caused them to be completely stripped from the final template DOM tree during the Render3 template transform and pushed into standard component stylesheets. By limiting the style element parsing to standard 'style' tags, namespaced SVG style tags remain safely in the template AST as normal DOM elements, preserving local SVG styling.

Closes #68977

(cherry picked from commit ec138c3645)
2026-05-28 14:02:52 +02:00
Bhuvansh855 8538bdce1c docs: fix grammar issues in resource guide
(cherry picked from commit 0e6cb4151c)
2026-05-28 13:48:06 +02:00
Yenya030 582a417bd2 fix(http): exclude withCredentials requests from transfer cache
Update the transfer cache check to safely exclude all requests sent with the `withCredentials` flag.

By default, the HTTP transfer cache avoids caching user-specific responses to prevent sensitive data exposure or incorrect caching. While requests with explicit headers like `Cookie` or `Authorization` are excluded by default, requests can also be sent with credentials via the `withCredentials` flag without having those headers explicitly declared on the request object.

To keep user-specific responses from being cached, exclude `withCredentials` requests unconditionally, even when the `includeRequestsWithAuthHeaders` option is set to true.

(cherry picked from commit 34090cb12e)
2026-05-27 14:13:21 -07:00
Yenya030 5c6d6df34b fix(http): skip TransferCache for cookie-bearing requests by default
Treat requests with a Cookie header like other auth-bearing requests and skip TransferCache caching them by default.

This preserves the explicit opt-in path via includeRequestsWithAuthHeaders, adds regression coverage for cookie-bearing requests, and updates the SSR guide to document the behavior.

(cherry picked from commit ab459798d9)
2026-05-27 14:13:21 -07:00
RonGamzu 29ceeffd40 docs: fix typos in source code comments
(cherry picked from commit 6f56202755)
2026-05-27 11:18:26 -07:00
Ricardo Chavarria 1a84668f0c docs(docs-infra): add Spanish community translation
Add https://docs.angular.lat/ (Español) to the community translations section.

(cherry picked from commit 48b4625fb3)
2026-05-27 11:16:59 -07:00
Kam 551a2a1f46 docs: fix preposition in libraries naming callout
The naming callout said the ng- prefix is "used from the Angular framework". Change to "used by", matching standard usage and the surrounding prose.

(cherry picked from commit 8c3e46fb53)
2026-05-27 11:09:46 -07:00
Harmeet Singh 84c6579a4e docs: clarify signals effect import source
(cherry picked from commit 741fcc4abf)
2026-05-27 11:08:54 -07:00
Kam 2b17b2db88 refactor(language-server): drop duplicate isAngularCore helpers in session
session.ts defined isAngularCore, isExternalAngularCore, and
isInternalAngularCore as byte-identical copies of the already-exported
versions in utils.ts. Only isAngularCore was used locally; the other
two were dead. handlers/template_info.ts already imports the utils
version. Remove the duplicates and import isAngularCore from utils.

(cherry picked from commit d808866f89)
2026-05-27 10:52:54 -07:00
Bhuvansh855 fd3573d99d fix(docs-infra): improve inline code layout
Remove inline-block layout behavior from inline code elements
to improve wrapping and spacing in multiline documentation
paragraphs.

(cherry picked from commit fdf0bf9a62)
2026-05-27 10:52:19 -07:00
Joey Perrott ef38852213 ci: configure setup and use pnpm in benchmark comparison workflow
The benchmark comparison workflow fails because it runs pnpm install
without setting up node and pnpm first. We configure the setup steps
manually so that checkouts from forks are supported.

Additionally, we update the benchmark comparison script (index.mts)
to use pnpm rather than hardcoded yarn commands to install
dependencies when checking out revisions.

(cherry picked from commit a648e8e914)
2026-05-27 10:51:43 -07:00
Alan Agius 2232a62bb2 fix(dev-infra): draft GitHub release to support immutable releases
Update the release tool to create the GitHub release in a draft state initially and publish it only after the extension asset (.vsix) has been successfully uploaded.

GitHub shifted towards immutable releases. If a release is published instantly upon creation,the assets will not be able to be uploaded.

(cherry picked from commit 26f4ed5056)
2026-05-27 10:50:26 -07:00
Andrew Scott ad5053b518 fix(zone.js): avoid type error on custom object rejection with rejection property
Ensure that when a custom object with a 'rejection' property is thrown as a raw promise rejection, the unhandled promise rejection error logger does not crash with a TypeError while trying to access undefined zone properties.

Also wrap microtask queue draining and task frame counter updates with defensive try-finally blocks to guarantee internal scheduler states are properly reset under any potential call stack exception unwinding scenarios.

(cherry picked from commit fa7580061b)
2026-05-27 10:45:19 -07:00
SkyZeroZx ca32fc1000 fix(service-worker): Preserves HTTP cache mode in asset group requests
Ensures explicit HTTP cache mode from incoming requests is forwarded and maintained when creating fetch requests for assets, aligning with expected fetch behavior and preventing unintended cache handling.

(cherry picked from commit 31399c2171)
2026-05-27 10:43:19 -07:00
SkyZeroZx b8bd49341d fix(service-worker): Preserves explicit 'credentials: omit' in asset requests
Ensures that explicitly provided `credentials: 'omit'` options are preserved
when creating new requests, preventing unintended credential inclusion.

(cherry picked from commit 5b0e9663e5)
2026-05-27 10:43:19 -07:00
leonsenft 251c8f2740 test(core): remove obsolete SVG script sanitization translation test (#68925)
Removes the `should throw error on translated SVG script ResourceURL
attributes` integration test from `security_integration_spec.ts`.

This test is now obsolete because SVG `<script>` elements are stripped during
template compilation (implemented in 90494cd909). As a result, they are no
longer present in the compiled template to trigger runtime sanitization,
causing this test (which expected a sanitization error to be thrown) to fail.

PR Close #68925
2026-05-27 10:42:29 -07:00
Alan Agius dada86e43d fix(core): synchronize core sanitization schema with compiler (#68925)
Synchronizes the core's copy of the DOM security schema with the compiler-side schema definitions.

PR Close #68925
2026-05-27 10:42:29 -07:00
Alan Agius 782e01594e fix(compiler): strip namespaced SVG script elements during template compilation (#68925)
Ensures that namespaced <script> elements (such as :svg:script) are correctly classified as PreparsedElementType.SCRIPT by the template preparser and stripped during compilation to prevent potential XSS vulnerabilities. Consequently, obsolete security schema mappings and runtime sanitization checks for <script> attributes have been removed since these elements are never present in compiled template outputs.

PR Close #68925
2026-05-27 10:42:29 -07:00
Alan Agius ff12fe55ac fix(core): normalize tag names in runtime i18n attribute security context lookup (#68925)
Normalize namespaced tag names (e.g., :xhtml:a to a) inside i18nResolveSanitizer before looking up their security context. This ensures custom namespaced tag attributes undergo correct translation sanitization at runtime.

PR Close #68925
2026-05-27 10:42:29 -07:00
Alan Agius 0b07f47bd6 fix(compiler): normalize tag names with custom namespaces in DomElementSchemaRegistry (#68925)
Custom XML/XHTML namespaced elements (e.g., <xhtml:a>) fall back to the standard HTML namespace during element creation at compile-time/runtime. However, their property and security context lookups inside the schema registry were incorrectly performed using the full namespaced tag name (e.g., :xhtml:a), which bypassed the default a|href sanitization registry and incorrectly returned SecurityContext.NONE instead of SecurityContext.URL.

This commit introduces tag name normalization inside DomElementSchemaRegistry for custom namespaces (other than the built-in svg and math namespaces). Custom namespaced tag names are now normalized to their simple HTML element counterparts for all registry queries, ensuring that correct property schema validation and dynamic security sanitization rules (such as URL sanitization) are enforced at runtime.

PR Close #68925
2026-05-27 10:42:29 -07:00
Alan Agius cc1378d54b fix(compiler): sanitize dynamic href and xlink:href bindings on SVG a elements (#68925)
Dynamic bindings to `href` and `xlink:href` attributes on SVG `<a>` elements (`<svg:a>`) were previously unmapped in the DOM security schema. As a result, they bypassed sanitization completely, creating a potential XSS vulnerability if bound to untrusted user inputs (e.g., `javascript:` URLs).

This fix mitigates this risk by:

1. Registering `href` and `xlink:href` on `<svg:a>` elements under the `SecurityContext.URL` context in both the compiler and core DOM security schemas.

2. Enabling template compilation to output runtime URL sanitization checks (`ɵɵsanitizeUrl`) on these attributes.

3. Adding regression and verification test cases to ensure dynamic SVG link bindings are safely sanitized at runtime while static values are correctly allowed.

PR Close #68925
2026-05-27 10:42:29 -07:00
Alan Agius daaf32937f fix(core): support prefix-insensitive DOM schema lookups and compile-time i18n attribute validation (#68925)
Updates `DomElementSchemaRegistry` to strip `:svg:` and `:math:` namespace prefixes
from tag names before querying `SECURITY_SCHEMA` at compile-time. This allows SVG
and MathML attributes to correctly match their security contexts during compilation.

PR Close #68925
2026-05-27 10:42:29 -07:00
Matthieu Riegler 37e8aadf87 fix(platform-server): prevent SSRF bypasses via backslash URLs in HttpClient
Encoding backslashes ensures that they are not normalized to slashes and where they could generate a protocol relative URL.

(cherry picked from commit 140c4d04cb)
2026-05-27 10:23:34 -07:00
Alan Agius 72696e244e fix(platform-server): secure location and document initialization against SSRF and path hijack
Normalizes the URL and path parsing logic inside platform-server by consolidating security checks and normalizations into a single, unified parseUrl helper function.

This includes:
- Collapsing multiple consecutive leading slashes and backslashes (e.g., // or /\) to a single forward slash to avoid protocol-relative parsing of path-like & relative inputs.
- Rejecting malformed absolute URLs that are otherwise accepted by lenient DOM parsers like Domino but rejected by standard WHATWG parsers, preventing SSRF / allowedHosts validation bypasses.
- Ensuring parseDocument gets the fully parsed and normalized URL instead of raw, unvalidated configuration values, preventing virtual document hostname adoption/origin hijack.
- Moving parseUrl unit tests into a dedicated url_spec.ts test file to keep platform_location_spec.ts clean and decoupled.

(cherry picked from commit 1307ff355c)
2026-05-27 10:22:42 -07:00
Matthieu Riegler 300f61feb3 fix(common): sanitize placeholder
The placeholder should be sanitized to prevent CSS/content injection.

(cherry picked from commit b56e865148)
2026-05-27 10:21:55 -07:00
Matthieu Riegler 7f4ac78994 fix(common): add upper bounds for digitsInfo
The prevents the `roundNumber` function from allocating a large array.

(cherry picked from commit dfdfbe34a5)
2026-05-27 10:21:02 -07:00
Matthieu Riegler e6fe77cc97 fix(core): sanitize meta selectors
Ensure that property/name are correctly escaped and doesn't break out of the intended selector.

(cherry picked from commit d5a489aed3)
2026-05-27 10:19:01 -07:00
Andrew Scott 525e1605a6 release: bump VSCode extension version to 21.2.4 2026-05-22 14:25:49 -07:00
Andrew Scott 4a41831326 fix(vscode-extension): disable language server in untrusted workspaces
Restrict untrusted workspace support to limited mode. Skip launching the language client and registering commands in restricted mode, and only start them once workspace trust has been explicitly granted.
2026-05-22 13:08:32 -07:00
Andrew Scott 6d8b156b45 fix(vscode-extension): restrict jsdoc markdown trust and harden document opening
Restrict JSDoc hover links to the custom openJSDocLink command and implement document
opening using safe workspace APIs.
2026-05-22 10:08:44 -07:00
Andrew Scott 82cf38ad95 fix(vscode-extension): prompt for confirmation before loading workspace tsdk
Harden the typescript.tsdk and js/ts.tsdk.path settings loading
in the VS Code extension client.

This change hardens tsdk loading by:
1. Ignoring workspace-level tsdk paths in untrusted workspaces.
2. Prompting the user for explicit confirmation before loading a
   workspace-level tsdk path in trusted workspaces, and saving the
   approval state in a secure, local workspaceState.
2026-05-22 10:05:07 -07:00
Andrew Scott 711f873e31 refactor(vscode-extension): Remove effectively dead code
Since bundled path is at the start of probe locations, it's always going to be found first.
Workspace versions will never be used. getProbeLocations is effectively dead and confusing code.

(cherry picked from commit d8c871ef80)
2026-05-22 09:58:27 -07:00
leonsenft 3fe8562b38 release: cut the v21.2.14 release 2026-05-20 15:34:51 -07:00
Kam 9627e02bde docs: link to ng new reference from installation guide
The installation guide walks a developer through `ng new <project-name>` but provides no link to the `ng new` CLI reference, leaving every option the command supports undiscoverable from the install flow. Link `ng new` in the prose to the reference page so options are one click away.

(cherry picked from commit 04f31cce3e)
2026-05-20 14:09:38 -07:00
arturovt a7b9ff5a58 docs: document FormBuilder.group() controlsConfig value shapes
The `@param` JSDoc for `FormBuilder.group()` previously described the argument only as “a collection of child controls”, without explaining the four supported value shapes:

* a raw value
* a `FormControlState`
* a `ControlConfig` tuple
* a pre-built `AbstractControl`

The fact that the second element of a `ControlConfig` tuple can accept `AbstractControlOptions` (for example to configure per-control `updateOn`) was especially non-obvious and undocumented.

This change adds a `@usageNotes` section with concrete examples covering each supported shape.

Closes #43984

(cherry picked from commit 3b8503f960)
2026-05-20 14:07:59 -07:00
Kam 1b6f780e2d docs: fix grammar slips on pipes guide
Fixes subject-verb agreement in the overview opener and date/currency example, a singular pronoun for a plural antecedent under change detection, and an "a object" -> "an object" a/an slip.

(cherry picked from commit 41a772ec0b)
2026-05-20 13:51:54 -07:00
arturovt d56f1a35ad docs: document barrel file caveat for @defer lazy chunks
Users often enable @defer expecting a separate lazy chunk but don't get
one, with no obvious error to explain why. The root cause is almost
always a barrel file import — the bundler treats the whole barrel as a
single module and can't split out individual exports.

Add a section to the defer guide that starts from the symptom (no lazy
chunk), shows the barrel import pattern that causes it, and gives the
direct-import fix.

Closes #52554

(cherry picked from commit d985957f09)
2026-05-20 13:47:47 -07:00
arturovt 232b21db55 docs: document content projection limitations
Add a Limitations section to the content projection guide covering two
common footguns that aren't obvious from the feature description alone.

First, projected content lives in the declaring component's view, not
the receiving component's. This means OnPush on the receiving component
doesn't prevent projected content from being checked on every parent
cycle, and projected content can't see the receiving component's
viewProviders.

Second, some library components (menus, tabs, lists) use ContentChildren
to wire up keyboard navigation and ARIA behavior and assume they own
their children directly. Projecting external content into them tends to
break that behavior silently.

Closes #49679

(cherry picked from commit de9e3d136e)
2026-05-20 13:31:50 -07:00
arturovt 1d6e71dd78 docs: clarify ngDoCheck invocation behavior with OnPush strategy
The previous documentation for `DoCheck` / `ngDoCheck` implied that the
default change-detector had run on the directive itself, which is
misleading. `ngDoCheck` is actually invoked when the *parent's*
change-detector checks the directive's input bindings — meaning it fires
even for `OnPush` components whose own change detection was skipped.

Updated three places in lifecycle_hooks.ts:
- Interface description: scopes "the check" to input bindings in the
  parent template and adds an explicit OnPush callout.
- "detects changes" clarified to "detects changes to the directive's
  input bindings".
- Method description: "after the default change-detector runs" →
  "after the default change-detector has checked the directive's input
  bindings in the parent template".

Fixes #48140

(cherry picked from commit ca44055166)
2026-05-20 11:12:11 -07:00
Kam 0c7f70e8ea fix(docs-infra): make absolute angular.dev hrefs relative in CLI option descriptions
CLI option descriptions are sourced from `@angular/cli` schema JSON
files, several of which contain absolute `https://angular.dev/...` URLs
in their `description` text. Those URLs render with the external-link
icon and push preview users out to production when viewed on
`next.angular.dev` or other dev previews. The path bypasses the existing
`link.mts` ban on absolute angular.dev links because option descriptions
go through `marked.parse` directly, without `AdevDocsRenderer`. Rewrite
the rendered hrefs whose values begin with `https://angular.dev/` (or
the `http:` variant) to root-relative paths so the resulting anchors
route through Angular's Router and resolve against the active
deployment. Subdomains such as `next.angular.dev/...` are intentionally
not rewritten because they refer to genuinely different deployments.

Closes #68795

(cherry picked from commit 745ee71c25)
2026-05-20 10:28:32 -07:00
Alan Agius 1ef4ea3e39 docs: update supported Angular versions table to reflect v19 end-of-life status
(cherry picked from commit b70515cada)
2026-05-20 10:10:01 -07:00
arturovt 395919ffeb docs: clarify viewProviders visibility with projected content
The providers vs. viewProviders section explained what happens but not
why — specifically, why projected content can still access a parent
component's viewProviders. Added an explanation that DI follows where
content was declared, not where it's rendered, so projecting a component
into a child's ng-content cuts off the child's viewProviders but leaves
the declaring component's viewProviders reachable.

Closes #49202

(cherry picked from commit 03161dc114)
2026-05-20 10:09:07 -07:00
tmpln 49113ac0ef fix(core): visit ICU expressions in signal migration schematics
Before this fix ICU expressions were not migrated.

(cherry picked from commit 048817dfa7)
2026-05-19 13:58:13 -07:00
aparziale 027c7c0c95 docs: update HTTP testing setup guidance
Update testing documentation clarify HttpClient testing providers

Fixed #68792

(cherry picked from commit 8ebc900067)
2026-05-19 13:41:17 -07:00
g.turri 7f444e1c7f docs: Fix accepted Provider type in doc snippet
out of the box my IDE tells me there an error on

    const testProviders: Provider[] = [provideHttpClient(), provideHttpClientTesting()];

because `provideHttpClient()` returns an `EnvironmentProviders` so
I can't put it in a variable of type `Provider[]`

(cherry picked from commit 4e55ceafc9)
2026-05-19 13:19:38 -07:00
june-by 34f89fb6c2 docs(compiler): add typeCheckHostBindings option to angular compiler options
(cherry picked from commit c49661b57b)
2026-05-19 13:09:30 -07:00
Alan Agius 68282dff9f fix(compiler): strip namespaced SVG script elements during template compilation
Ensures that namespaced <script> elements (such as :svg:script) are correctly classified as PreparsedElementType.SCRIPT by the template preparser and stripped during compilation to prevent potential XSS vulnerabilities. Consequently, obsolete security schema mappings and runtime sanitization checks for <script> attributes have been removed since these elements are never present in compiled template outputs.

(cherry picked from commit 90494cd909)
2026-05-19 13:06:05 -07:00
arturovt 099bf577ee fix(router): skip scroll-to-top on initial navigation when hydrating
When scrollPositionRestoration is enabled and the app hydrates an
SSR-rendered page, RouterScroller was unconditionally scrolling the
viewport to [0, 0] on the first imperative navigation. This discards
any scroll position the user established while the server-rendered
page was loading.

Fix by injecting IS_HYDRATION_DOM_REUSE_ENABLED into RouterScroller
and suppressing the scroll-to-top for the initial navigation only.
Subsequent navigations are unaffected.

Closes #64578

(cherry picked from commit 8ec0d1eee8)
2026-05-19 13:01:59 -07:00
Alan Agius fd05135da9 test(upgrade): exclude unit test files from E2E application sources
Exclude `**/*.spec.ts` files from the `srcs` glob of the `full_sources` target.

Previously, `module.spec.ts` was compiled as part of the application's main sources because the glob pattern only excluded `**/*_spec.ts` (E2E specs). Consequently, `module.spec.js` was generated and included in the runfiles of the E2E test target, causing the Protractor runner to load and execute it. This failed since the E2E testing runner does not have access to unit testing imports like `@angular/core/testing`.

(cherry picked from commit 7390af78b1)
2026-05-19 12:57:52 -07:00
tmpln c0f52272ed fix(core): do not insert todo when migrating void @Output
The following:

`@Output() someChange = new EventEmitter<void>();`

is correctly migrated to:

`readonly someChange = output<void>();`

However, a TODO is incorrectly inserted for subsequent emissions from
`someChange`, stating that an argument is expected.

(cherry picked from commit 16fe27bfef)
2026-05-18 13:25:05 -07:00
Matthieu Riegler d1736efc32 docs(docs-infra): Show function args
With this change non-overloaded functions also show the params + return type in a dedicated block.

(cherry picked from commit 872853fbcb)
2026-05-18 13:22:33 -07:00
Kam 73b0ada729 docs: open external anchors in adev markdown in a new tab
Several raw HTML `<a>` anchors in adev markdown link to external
sites without `target="_blank"`, so they open in the same tab
instead of a new one like the rest of the site's external links.
Add `target="_blank"` to match.

(cherry picked from commit b7255f9d13)
2026-05-18 13:18:25 -07:00
Alan Agius 0fb2724194 fix(core): reject script element as a dynamic component host
To enhance application security and prevent accidental or malicious script execution, this change ensures that dynamically mounting a component via createComponent directly onto a <script> element throws a runtime error in development mode. SVG <script> elements are also rejected. The error message is designed to be fully tree-shakable under production builds where ngDevMode is disabled.

(cherry picked from commit 0011664d1c)
2026-05-18 13:16:35 -07:00
Alan Agius 6652ec0115 refactor(core): align namespaced attribute validation and security schema contexts
Refactors the element security schema lookups and runtime attribute validation to
consistently account for SVG and MathML namespaces. This improves the modularity
and accuracy of security context mapping during template compilation and runtime
constant evaluation, eliminating redundant or false-positive lifecycle checks.

(cherry picked from commit cef4a095a2)
2026-05-18 13:09:44 -07:00
SkyZeroZx 938a7f3edd fix(core): makes resource URL sanitizer lookup case-insensitive
Ensures the resource map for URL sanitization is queried using lowercase tag and property names, improving robustness by handling case variations consistently.

(cherry picked from commit 00c284015c)
2026-05-18 13:07:39 -07:00
Andrew Scott fc434c1d0a refactor(compiler-cli): Remove unused properties of IndexedComponent interface
These properties aren't used in the Kythe indexer and can be removed

(cherry picked from commit 13911b156b)
2026-05-15 10:38:02 -07:00
Matthew Beck 8282c09e2d release: cut the v21.2.13 release 2026-05-13 16:24:31 -07:00
Ben Hong 1e079a8994 docs: add clarification around plain object models
Co-authored-by: Matthieu Riegler <kyro38@gmail.com>
(cherry picked from commit 3584eeb491)
2026-05-13 11:24:39 -07:00
Paul Gschwendtner 7ab78d5c89 ci: mark devversion as unavailable
Currently OOO and I don't want reviews to be necessarily stuck for too long.

(cherry picked from commit 95034a7b92)
2026-05-12 10:47:13 -07:00
Kam 49a133aeaf refactor(compiler-cli): drop @ts-ignore around jsDocParsingMode
The getters and setters for jsDocParsingMode in `host.ts` and
`ts_create_program_driver.ts` were suppressed with @ts-ignore to
support TypeScript 5.2, which lacked the property on `ts.CompilerHost`.
The minimum supported TypeScript is now 6.0, and `jsDocParsingMode`
is part of the public TypeScript API, so the suppressions can go.

(cherry picked from commit 7a146238ba)
2026-05-11 12:40:29 -07:00
Jessica Janiuk c08321988d ci: update pullapprove
This removes thePunderWoman from active review requests, but leaves passive on.

(cherry picked from commit 43a8df9520)
2026-05-11 12:37:50 -07:00
Ben Hong c93d158aae docs: add new signal forms field metadata guide
Co-authored-by: Matthieu Riegler <kyro38@gmail.com>
(cherry picked from commit ef134ac367)
2026-05-11 12:05:32 -07:00
Kam 327edb9001 docs: add inject() example to "Forwarding injected dependencies"
Lead the section with the recommended `inject()` pattern (child
inherits the property, no `super` forwarding), and keep the existing
constructor DI example after as the alternative. Also fixes a typo
where the verb "class" should read "pass".

(cherry picked from commit 4ec076e13c)
2026-05-11 12:02:49 -07:00
Kam 3ec0a10ca0 docs: recommend output() over EventEmitter in reactive forms guide
The "Save form data" step pointed at `EventEmitter` while the rest of
the guide uses modern APIs (e.g. `inject(FormBuilder)`). Swap to
`output()` and align the TODO in the profile-editor example.

(cherry picked from commit 0629e7e505)
2026-05-11 12:02:08 -07:00
arturovt 0b7192f441 fix(platform-server): forward BEFORE_APP_SERIALIZED errors to ErrorHandler
Errors thrown by BEFORE_APP_SERIALIZED callbacks were previously logged
via console.warn and silently ignored. This meant failures such as
TransferState.toJson() encountering a circular reference would go
unreported in apps that use a custom ErrorHandler (e.g. Sentry).

Errors are now forwarded to the application's ErrorHandler, making them
visible through whatever reporting mechanism the app has configured.
The render continues to completion after the error is reported.

Closes #65811

(cherry picked from commit 7623580378)
2026-05-08 14:10:15 -07:00
Kam 5000a6d2c0 docs: fix two 404 links in the roadmap
"Introduce built-in control flow" => guide/templates/control-flow (was
the now-removed next.angular.dev/essentials/conditionals-and-loops),
and "Improve documentation and schematics for standalone components"
=> essentials/components (was the bare `components`, not an adev route).

(cherry picked from commit 8b46492b7e)
2026-05-08 14:05:45 -07:00
Alan Agius f9a58c1da3 docs: remove note regarding lack of support for ng test --debug in browser mode
This is no longer the case.

Closes #68621

(cherry picked from commit b3de3af0dd)
2026-05-08 08:57:13 -07:00
Angular Robot 6ed6498854 docs: update cross-repo adev docs
Updated Angular adev cross repo docs files.
2026-05-07 16:43:58 -06:00
Alan Agius 629905d537 fix(platform-server): add allowedHosts option to renderModule and renderApplication
In server-side rendering (SSR) setups, passing request URLs directly to the lower-level rendering APIs `renderModule` or `renderApplication` can expose applications to Server-Side Request Forgery (SSRF) or Host Header Injection attacks via absolute-form request URLs.
To mitigate these vulnerabilities at the framework layer, this commit introduces the `allowedHosts` option to `PlatformConfig` (supporting exact hostnames, wildcards like `*.example.com`, or `*` to allow all).

During platform initialization inside `createServerPlatform`, the hostname of the request `url` is validated against the `allowedHosts` list. If the hostname is not authorized, bootstrap immediately throws a host validation error, preventing unauthorized rendering and silent SSRF bypasses.

Closes #68436

(cherry picked from commit 60552a73e8)
2026-05-07 15:30:07 -07:00
Matthew Beck baf92da96e test: remove invalid css that was causing issues with the postcss parser
These tests happened to use garbage "{c}" declaration lists which caused
the parser to choke. Given that we already have tests demonstrating
similar behavior and that's not what these tests were meant to
demonstrate, I've updated them to use empty declaration lists.

(cherry picked from commit b1699da827)
2026-05-07 15:20:20 -07:00
Alan Agius 1c6553e97d fix(core): disallow event attribute bindings in host bindings unconditionally
Moves the event attribute validation check outside of `ngDevMode` in the `elementAttributeInternal` instruction to ensure that bindings to event attributes like `on*` are always blocked at runtime.

(cherry picked from commit 5b421c61cd)
2026-05-07 15:19:26 -07:00
Andrew Scott c39f7708a6 refactor(compiler): Update indexer API to be generic
Rather than requiring TS AST in the indexer API, this update makes it generic with adapters to provide necessary information. This allows other analysis pipelines that don't use TS AST to work with the indexer.

(cherry picked from commit bc655d006f)
2026-05-07 15:17:04 -07:00
Kam 2a1e6ec1bb docs: normalize product name casing across docs
Several user-facing docs, tooltips, and tutorial code samples used
non-canonical spellings of product names. This normalizes them to
the form each project uses for its own brand.

(cherry picked from commit ed333c3992)
2026-05-07 15:09:49 -07:00
Bhuvansh855 73ba918cce docs(animations): improve grammar and clarity across animation guides
(cherry picked from commit dc4b3172df)
2026-05-07 15:03:46 -07:00
SUMIDA, Ippei bca94ee0bb docs: Update error display for password field in signal forms playground
Change error message display from paragraph to list format in signal forms playground.

(cherry picked from commit 2fcfffbc7d)
2026-05-07 14:53:12 -07:00
Alex Rickabaugh 5316753957 release: cut the v21.2.12 release 2026-05-06 15:47:48 -07:00
Matthieu Riegler 9e38ed7d57 fix(core): sanitizer typings
This is to fix patch builds
2026-05-05 17:07:03 -07:00
Sam Severance 335d8c04dc docs: clarify @for track expression scope to include properties and methods
(cherry picked from commit 2e0ca49f32)
2026-05-05 17:02:57 -07:00
Kam 161d98e7f2 docs: correct "Angular JS" to "AngularJS"
"AngularJS" is the official product name for the v1.x line and is
written as a single word. A few places in the docs and package
READMEs used "Angular JS" with a space. This normalizes those
references to the canonical spelling.

(cherry picked from commit 8a7f955f0f)
2026-05-05 12:42:45 -07:00
Leon Senft 03ad53863b fix(forms): prohibit concurrent submits in signal forms
Prohibit concurrent submits in signal forms to prevent duplicate actions and side effects when a submission is already in progress.

If `submit()` is called while a prior submit is in progress for the same field or any of its parents, it returns `false` immediately without running the action again.

This commit also updates the documentation in `form-submission.md` to reflect this behavior.

Fixes #68317

(cherry picked from commit 708631f2c4)
2026-05-05 11:14:08 -07:00
Kam 929ec53578 fix(docs-infra): mask page content leak at mobile nav right edge
On phones, the page content behind the primary-nav drawer leaks 1-2px past its right edge. Mask it with a 2px var(--page-background) box-shadow.

(cherry picked from commit 1da396fd84)
2026-05-05 09:31:59 -07:00
Kam e6ab5eca57 fix(docs-infra): keep mobile nav drawers usable on small screens
On phones, opening the primary-nav drawer left the page behind it scrollable, and the secondary drawer's mask had no explicit height so long submenus got clipped above the page content. Lock the page with overflow: clip on :host:has(.adev-nav-primary--open) for phone-only (preserves the primary nav's sticky context), give the secondary mask height: 100dvh on tablet-landscape-down so it fills the visible viewport, and align the nav-list :host height to 100dvh too so its inner scroll matches.

(cherry picked from commit 441a00d665)
2026-05-05 09:31:31 -07:00
Kristiyan Kostadinov 3430251fef fix(core): i18n flags leaking on errors
The i18n sub-system has the `changeMask` and `changeMaskCounter` flags which are set by i18n-related instructions and reset once the state is applied. The problem is that if something throws within the application logic, the flags would never be reset. This is currently causing flakes in our CI runs.

These changes resolve the issue by adding a try/finally around the flags.

(cherry picked from commit 6339d264eb)
2026-05-05 09:30:59 -07:00
Sam Severance ce7a43a5b6 refactor(forms): improve clarity in SelectMultipleControlValueAccessor.writeValue
Rename the _optionMap forEach parameter from `o` to `id` and tighten its
type from `any` to `string`, removing the now-redundant `.toString()` call.

(cherry picked from commit cd20dd07ce)
2026-05-01 16:02:40 -07:00
Nikolaos G. Ntaiko af74b420e1 Fix typo in computed signals section
(cherry picked from commit 429c665901)
2026-05-01 16:00:52 -07:00
tmpln c37f6ca42f fix(core): visit ng-let expression value in signal migration schematics
Before this fix, references to inputs inside @let statements were not
accounted for.

(cherry picked from commit 0ea27f4e65)
2026-05-01 15:59:20 -07:00
Kam aba59aac59 fix(docs-infra): exempt form validator names from API auto-linking
Add required, pattern, min, max, minLength and maxLength to LINK_EXEMPT
so FieldState property names stop auto-linking to the validator
functions of the same name.

(cherry picked from commit c2f7403774)
2026-05-01 15:58:48 -07:00
Matthew Beck c2b441af6d test: add NgModule compliance test with 'bootstrap' & local compilation
There was not a test demonstrating local compilation with the
'bootstrap' param on NgModule. This test adds one, among other NgModule
fields in one. These other fields are broadly covered already, but this
rolls them into one test exercising all fields.

(cherry picked from commit 0fa8f98f4f)
2026-05-01 15:58:10 -07:00
cexbrayat fe13bb669d fix(core): allow explicit read generic with signal input transforms
Using explicit single generic arguments with transforms (for example, input<boolean>(false, {transform: booleanAttribute})) previously failed overload resolution.

Before this fix, type-checking produced:
````
✘ [ERROR] TS2769: No overload matches this call.
  Overload 1 of 5, '(initialValue: boolean, opts?: InputOptionsWithoutTransform<boolean> | undefined): InputSignal<boolean>', gave the following error.
    Type '(value: unknown) => boolean' is not assignable to type 'undefined'.
  Overload 2 of 5, '(initialValue: undefined, opts: InputOptionsWithoutTransform<boolean>): InputSignal<boolean | undefined>', gave the following error.
    Argument of type 'true' is not assignable to parameter of type 'undefined'. [plugin angular-compiler]
```

This change adds specialized overloads for explicit read generics.

(cherry picked from commit 1ab654cf28)
2026-05-01 15:54:01 -07:00
Angular Robot 08363f0f93 build: update dependency bazel_lib to v3.3.1
See associated pull request for more information.
2026-04-30 16:14:02 -07:00
Andrew Kushnir 05210ce2f1 ci: update PullApprove config
This commit updates the PullApprove config to keep myself as a reviewer, but do not auto-assign PR reviews.

(cherry picked from commit 2b624580e5)
2026-04-30 16:13:23 -07:00
Matthieu Riegler 3db96a5983 docs: add section about variable scopes
This is mostly a copy of the section we had on aio.

(cherry picked from commit 8f94892a71)
2026-04-30 16:10:33 -07:00
Angular Robot c2c869bbe5 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-04-30 16:09:55 -07:00
Angular Robot 7a6f4c2b4f build: update all github actions
See associated pull request for more information.
2026-04-30 16:09:31 -07:00
Matthieu Riegler 30cf85f71c refactor(common): update deprecation message
The old control flow is very much not recommended but we're not ready to remove it in v22.

(cherry picked from commit 28d38b582c)
2026-04-30 16:07:02 -07:00
Alan Agius 7a05a9a71a fix(core): validate security-sensitive attributes in i18n bindings
Ensures that security-sensitive attributes (e.g., sandbox, allow) are correctly validated when applied through i18n-* dynamic attribute bindings, preventing potential policy bypasses.

Closes #68418

(cherry picked from commit 9d7a609458)
2026-04-30 15:47:17 -07:00
Andrew Scott dc9b40ebb5 refactor: attempt stronger automatic comment blocking
Updates the PR review skill to have stronger language against automatic comment posting
without explicit approval

(cherry picked from commit d919f9a13e)
2026-04-30 15:45:45 -07:00
Sonu Kapoor 1aeebbe304 fix(core): respect ngSkipHydration on components with projectable nodes in LContainers
When a component is created dynamically via ViewContainerRef.createComponent
and receives projectable nodes (e.g. raw DOM nodes or embedded view root nodes),
applying ngSkipHydration to its host element did not prevent NG0503 from being
thrown during SSR serialization.

The root cause is an asymmetry in the serialization pipeline. For inline child
components, serializeLView already guards the annotateHostElementForHydration
call with a ngSkipHydration attribute check, so the component's lView is never
serialized when hydration is opted out. For components hosted inside an
LContainer (created via ViewContainerRef.createComponent), serializeLContainer
called serializeLView unconditionally — bypassing that guard entirely. When
serializeLView then encountered a projection slot backed by a raw DOM node
array, it threw NG0503 regardless of the ngSkipHydration flag.

The fix adds the same guard inside serializeLContainer before calling
serializeLView: if the child lView belongs to a component whose host element
carries ngSkipHydration, the lView serialization is skipped. This matches the
existing behavior for inline components and allows the documented workaround to
actually work for dynamically created ones.

Fixes #67928

(cherry picked from commit 4c9afb68a3)
2026-04-29 23:09:18 +00:00
Alon Mishne ef9669c1f8 release: cut the v21.2.11 release 2026-04-29 15:38:40 -07:00
Kam 6c6db5b604 docs(docs-infra): open update guide external links in new tabs
External links in the update guide opened inconsistently. Override
marked's link renderer to add `target="_blank" rel="noopener noreferrer"`
to external anchors and apply the `external-link-with-icon` mixin for
the icon. Convert raw HTML and bare URLs in recommendations.ts to
markdown so they all flow through the renderer.

(cherry picked from commit 13c0422029)
2026-04-29 20:59:46 +00:00
Kam 185862ef76 docs: document allowedHosts SSR requirement in v21 update guide
The GHSA-x288-3778-4hhx patch requires `allowedHosts` on
`CommonEngine` or SSR silently falls back to CSR. Add a checklist
item to the v21 update guide.

(cherry picked from commit 2101b13653)
2026-04-29 20:59:17 +00:00
Bhuvansh855 631e303dd9 docs: improve clarity in dependency injection guide
(cherry picked from commit 9f7d41f296)
2026-04-29 20:38:21 +00:00
Alan Agius 41e4851928 docs: update documentation for platform server URL token options
This `baseUrl` option is not available.

(cherry picked from commit 0f7086add4)
2026-04-29 20:37:04 +00:00
Andrew Scott a24dcfd1ec refactor(compiler-cli): remove reflectionhost from environment
all necessary info is already available in the tcb meta objects. environments without full ts program no longer need a reflectionhost for tcb generation

(cherry picked from commit c70625e806)
2026-04-29 20:36:25 +00:00
Ben Hong 1dde3827e9 docs: add debouncing section to signal forms async operations
Co-authored-by: Matthieu Riegler <kyro38@gmail.com>

Co-authored-by: Matthieu Riegler <kyro38@gmail.com>

Co-authored-by: Matthieu Riegler <kyro38@gmail.com>
(cherry picked from commit 18826de489)
2026-04-29 20:35:22 +00:00
Andrew Scott b40b67cdc1 fix(vscode-extension): Look for tsdk override in the new js/ts.tsdk.path setting
recent versions of vscode use js/ts.tsdk.path rather than typescript.tsdk

relates to #68423
2026-04-29 13:32:02 -07:00
Angular Robot 4900e453e1 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-04-29 13:29:44 -07:00
Matthieu Riegler 42d57c3578 refactor(common): fix viewport tests
10ad3c0 broke the 21.2.x branch
2026-04-28 16:38:11 -07:00
SkyZeroZx 10ad3c0692 fix(common): prevent focus from scrollToAnchor
Focus the target element using `focus({preventScroll: true})` after scrolling, so the browser doesn’t adjust the scroll position when applying focus.

Fixes #65938

(cherry picked from commit 97cac1cf4d)
2026-04-28 19:39:31 +00:00
Denis Balan d07f502946 docs: Fix links to Firebase AI Logic Angular example
(cherry picked from commit 5dfe37df8e)
2026-04-28 19:10:52 +00:00
Suraj Yadav 600da64ba4 docs(forms): add NG01902 error reference and link to docs
Add the NG01902 (Orphan field in signal forms) documentation page
to the Error Encyclopedia and change the ORPHAN_FIELD_PROPERTY
error code to -1902 so Angular's RuntimeError automatically appends
a link to angular.dev/errors/NG01902 in the thrown error message.

(cherry picked from commit f2c6445681)
2026-04-28 19:07:50 +00:00
Matthieu Riegler a40e2cebc8 fix(core): fix ordering of view queries metadata in JIT mode
AOT was generating an array that was ordered as signal queries first, then the decorator queries.
Aligning JIT with AOT fixes the issue illustrated by the test.

fixes #68404

(cherry picked from commit 8c11816490)
2026-04-28 19:03:45 +00:00
Kam 9ed1b6c045 docs: use contentChildren() in component harness example
The example already uses the signal-based input() but still declares
items with the @ContentChildren decorator. Convert to the signal-based
contentChildren() query for consistency.

(cherry picked from commit a6eb55642c)
2026-04-28 19:03:13 +00:00
Angular Robot 2b9b27e882 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-04-28 12:00:21 -07:00
Angular Robot 85f1c0a268 build: lock file maintenance
See associated pull request for more information.
2026-04-28 11:59:30 -07:00
Kam fb7e67ca9d docs: use inject() in @Self example in hierarchical DI guide
The surrounding @SkipSelf and @Host examples already use inject(),
and the section intro recommends it. Align the @Self example to match.

(cherry picked from commit 273ff07469)
2026-04-28 17:31:02 +00:00
Herdiyan IT Dev 316d49a8ae refactor(dev-infra): use shell: false and quote args in benchmark-compare workflow
Currently, the exec() utility uses childProcess.spawn() with shell: true. This commit changes the spawn option to shell: false to prevent OS command injection vulnerabilities and quotes the benchmark target in the github action.

(cherry picked from commit f219e65841)
2026-04-28 17:29:28 +00:00
Matthieu Riegler 9bcbf37641 refactor(core): fix bundling symbol test
The golden needed an update.
2026-04-28 09:44:58 -07:00
Simon a3d14fc11b docs: remove duplicated text
(cherry picked from commit c8aad6acc6)
2026-04-28 00:10:24 +00:00
Simon 0612c8c53b docs: remove spaces to correct the indentation
(cherry picked from commit 38c352766a)
2026-04-28 00:09:50 +00:00
Kristiyan Kostadinov 4f5d8a2c0b fix(compiler): let declaration span not including end character
Fixes that the span for `@let` declarations didn't include the end token.

(cherry picked from commit 6bd1721662)
2026-04-28 00:09:19 +00:00
Alan Agius be1f80a253 fix(platform-server): ensure origin has a trailing slash when parsing url
The origin did not have a trailing slash, which caused parsing issues for relative URLs.

Fixes #68322

(cherry picked from commit 2a6b6fafb0)
2026-04-28 00:08:40 +00:00
aparziale 76cf531002 docs: Fix typo in doc
Fix typo in documentation. Changed "create workspace" to "create a workspace"

Fixed #68375

(cherry picked from commit 982e3cce52)
2026-04-28 00:08:06 +00:00
Sonu Kapoor 885a1a1d97 fix(core): guard against non-object events and avoid listener wrapper identity mismatch
Two issues caused browser test failures after the event replay fix:

1. `markEventHandledForElement` used the event object as a WeakMap key, but
   `DebugElement.triggerEventHandler` can pass null or primitive values as the
   event argument. Added an early return for non-object values.

2. Registering a separate `domListener` closure with `renderer.listen` instead of
   `wrappedListener` caused `DebugElement.triggerEventHandler` to invoke the
   handler twice: once via `this.listeners` (which holds `wrappedListener`) and
   once via Zone.js's `eventListeners` (which holds the unwrapped `domListener`).
   The existing dedup logic in `triggerEventHandler` checks if the unwrapped
   Zone.js listener is already in `invokedListeners`, but with two different
   function objects that check always fails.

   Replaced the `domListener` wrapper with a property (`__ngNativeEl__`) stored
   directly on `wrappedListener`. `wrapListenerIn_markDirtyAndPreventDefault` reads
   this property and calls `markEventHandledForElement` when the listener fires,
   while `renderer.listen` receives the same `wrappedListener` function that
   Angular stores in `lCleanup`, preserving the dedup invariant.

(cherry picked from commit 3583c01bf9)
2026-04-28 00:07:41 +00:00
Sonu Kapoor 7a64aff9b5 fix(core): prevent event replay double-invocation when element hydrates before app stability
When `withEventReplay()` is enabled and a component hydrates before the
application becomes stable (e.g. while a pending HTTP request is in
flight), a user interaction on the hydrated element triggers both the
real DOM listener registered by Angular and the jsaction replay path.
This causes the event handler to be invoked twice.

The root cause is that `listenToDomEvent` registers the same
`wrappedListener` both as a stashed jsaction handler (via
`stashEventListenerImpl`) and as a native DOM listener (via
`renderer.listen`). When the user interacts after hydration but before
app stability, jsaction queues the event because no dispatcher is
registered yet. Once the app stabilises and `initEventReplay` runs,
jsaction replays the queued event through `invokeListeners`, which
calls the stashed handler a second time.

The fix tracks dispatched `(event, element)` pairs in a
`WeakMap<Event, WeakSet<Element>>`. The native DOM listener wrapper
records each pair via `markEventHandledForElement`, and `invokeListeners`
skips replay for any pair already present. Keying by element (rather
than event alone) preserves incremental hydration behaviour, where
jsaction legitimately replays the same event on a different element
(the deferred block content) from the one that originally triggered
hydration.

Fixes #67328

(cherry picked from commit d5fd51e956)
2026-04-28 00:07:40 +00:00
Kam cbaba8afe6 docs: document moduleResolution bundler change in v20 update guide
The v20 update guide doesn't flag that `ng update` switches
`moduleResolution` to `'bundler'`. Add a checklist item so manual
upgrades don't miss it.

(cherry picked from commit 67a5f00cd8)
2026-04-28 00:06:39 +00:00
Andrew Scott fbe080f829 docs(router): Fix method name for retrieving stored handles
method name was documented incorrectly. this fixes the name

(cherry picked from commit f17f32c351)
2026-04-28 00:03:25 +00:00
aparziale 1be52ad880 docs: Align Router API docs with inject based DI
Updates Router documentation examples to reflect modern inject based dependency injection instead of constructor injection.

Fixed #68378

(cherry picked from commit ded5a0ed62)
2026-04-28 00:02:32 +00:00
SkyZeroZx fa4eff36cb docs(docs-infra): Validate case-sensitive API symbol links in @link
Adds build-time validation for case-sensitive API symbols in `@link`. Avoid broken links

(cherry picked from commit d2c7b4e111)
2026-04-28 00:01:44 +00:00
Andrew Scott 27da56ee9d refactor: use stronger language for adev writing guide
use stronger language to improve skill usage hits for adev writing.

(cherry picked from commit 357cb15208)
2026-04-27 22:29:08 +00:00
Angular Robot 7b8faccc34 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-04-27 15:27:28 -07:00
Kam 6662724c99 docs(docs-infra): improve Playground card on installation page
Updates the Playground card copy and adds a `titleInline` attribute on
<docs-card> so the icon and title sit on the same row. Existing cards
are unaffected.

(cherry picked from commit 29081f7765)
2026-04-24 17:34:42 +00:00
Angular Robot 8ce8b9342a build: update pnpm to v10.33.2
See associated pull request for more information.
2026-04-24 10:11:20 -07:00
Angular Robot 131c422da2 build: update devinfra digest to c4d0c37
See associated pull request for more information.
2026-04-24 10:06:35 -07:00
Alan Agius fa33854d24 docs: document trustProxyHeaders and update X-Forwarded-Prefix validation
Update the security guide to explain how to configure `trustProxyHeaders` when initializing the application engine. Also, update the validation rules for `X-Forwarded-Prefix` to reflect that it must start with `/` and contain only alphanumeric characters, hyphens, and underscores.

(cherry picked from commit 0399115a82)
2026-04-24 17:04:00 +00:00
Angular Robot f3308dc1f7 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-04-23 14:14:19 -07:00
Angular Robot f9b70a0887 docs: update cross-repo adev docs
Updated Angular adev cross repo docs files.
2026-04-23 12:41:14 -07:00
Kam a8dd801856 docs: link Angular CLI in installation prerequisites
Links the "Angular CLI" mention in the Terminal prerequisite to the CLI
overview page.

(cherry picked from commit 6120d3196a)
2026-04-23 18:39:42 +00:00
Angular Robot 2ec753cb77 build: update devinfra digest to e9c1452
See associated pull request for more information.
2026-04-23 11:13:23 -07:00
Leon Senft 90cc755a56 release: cut the v21.2.10 release 2026-04-22 16:28:45 -07:00
Savio Dsouza 4fd1a08699 build: pin firebase-tools version and disable credential persistence in preview deploy workflow
(cherry picked from commit 8f5e0e09e9)
2026-04-22 14:38:27 -07:00
Angular Robot 750af5b123 build: update cross-repo angular dependencies to v21.2.8
See associated pull request for more information.
2026-04-22 11:03:02 -07:00
Rishabhdeep Singh 5533ab4f56 fix(migrations): fix NgClass leaving trailing comma after removal
This fixes an issue where when removing NgClass from the imports array of a component, an extra trailing comma would be left behind if it was the last element in that component`.

(cherry picked from commit b395173cf2)
2026-04-22 09:59:54 -07:00
Rishabhdeep Singh 2b9954fd3d fix(migrations): fix NgClass leaving trailing comma after removal
This fixes an issue where when removing NgClass from the imports array of a component, an extra trailing comma would be left behind if it was the last element in that component`.

(cherry picked from commit 27f021248d)
2026-04-22 09:59:54 -07:00
Angular Robot 4dc7bf5a75 build: lock file maintenance
See associated pull request for more information.
2026-04-21 10:23:48 -07:00
Joel Kesler 0d5ee9ae1b fix(docs): link formatting in "Animating your Application with CSS"
One of the links in `Animating your Application with CSS` page has a formatting bug for one of it's links.

(cherry picked from commit b24b4cb699)
2026-04-21 10:20:11 -07:00
Nikhil Bachani 6a02320575 docs: fix tracking expression reference in NG0955.md
Corrected the tracking expression reference from 'item.key' to 'item.value' in the explanation of duplicate keys.

(cherry picked from commit ac92a8aae8)
2026-04-21 10:14:53 -07:00
Angular Robot 751e4af80b build: update cross-repo angular dependencies
See associated pull request for more information.
2026-04-21 09:04:23 -07:00
Matthieu Riegler da346bf696 docs: update builder docs
We use `@angular/build` today.

(cherry picked from commit fa6a3d208d)
2026-04-21 09:02:37 -07:00
SkyZeroZx 580212c995 fix(router): restore internal URL on popstate when browserUrl is used
Fixed an issue where back/forward (`popstate`) navigation attempted to match the displayed `browserUrl` instead of the internal route, which could result in `NG04002: Cannot match any routes`.

Fixes #67549

(cherry picked from commit 6eff439546)
2026-04-20 16:46:30 -07:00
Kam a9ddc5d40a docs: warn against storing secrets in environment files
Add a CRITICAL callout warning that files in `src/environments/`
ship to the client and should not hold secrets like API keys.

(cherry picked from commit d27e2c24e1)
2026-04-20 13:29:18 -07:00
Kam 0d08d9cc82 docs(docs-infra): guard sandbox reset before initialization in playground
changeTemplate() was calling reset() on the sandbox before init()
completed, causing a TypeError when spawning processes on an
uninitialized WebContainer. Add isSandboxReady signal to skip
reset until the sandbox is fully initialized.

(cherry picked from commit c04c0b977a)
2026-04-20 13:17:22 -07:00
Kam 4c7ec66807 docs(docs-infra): adjust close button spacing in mobile navigation
Use relative positioning to offset the close button from the top edge without affecting the layout of surrounding elements.

(cherry picked from commit 2dc3ab596b)
2026-04-20 13:14:40 -07:00
Matthieu Riegler 62266eee8b ci: remove disabled side-effects integration tests
This test was disabled 5+ years ago, we probably don't need it anymore.

(cherry picked from commit 13be2961f6)
2026-04-20 13:13:28 -07:00
Bhuvansh855 2a50dceef5 docs: improve wording and consistency in forms documentation
(cherry picked from commit 74a7d6b8f9)
2026-04-20 13:12:35 -07:00
Andrew Scott c9215b3539 Revert "refactor(core): complete removal of deprecated createNgModuleRef alias"
This reverts commit d88d6ed69e.
Depended on a PR that was not merged to 21.2.x
2026-04-20 12:55:55 -07:00
SkyZeroZx d88d6ed69e refactor(core): complete removal of deprecated createNgModuleRef alias
Finalize the cleanup by removing the remaining `createNgModuleRef` alias.

(cherry picked from commit 3ae40e6685)
2026-04-20 12:09:51 -07:00
Bhuvansh855 e5b93ea4ca docs: fix wording in reactive forms guide
(cherry picked from commit c610425310)
2026-04-20 09:51:47 -07:00
Joey Perrott ce883d95ef build: update peer dependencies and bump version
Update peer dependencies to support Angular 21 instead of 22-next and bump the patch version to 0.21.1.
2026-04-20 09:39:10 -07:00
Bhuvansh855 be7490964a docs: improve clarity in dynamic forms guide
(cherry picked from commit a718e188c6)
2026-04-20 09:30:50 -07:00
Angular Robot 810fb7382f build: update cross-repo angular dependencies
See associated pull request for more information.
2026-04-17 14:34:29 -07:00
aparziale b24ead5571 refactor: Improve hydration mismatch errors for third-party scripts
Improves error messages shown during hydration mismatches to better
surface cases where third-party scripts or browser extensions have
modified the DOM outside of Angular's control.

Fixed #59224

(cherry picked from commit d771a65ac0)
2026-04-17 14:33:15 -07:00
pravintargaryen 6d10b8bb95 docs: add inject to structural directive imports
(cherry picked from commit da6c92eccd)
2026-04-17 14:25:09 -07:00
Angular Robot c738d45fa5 build: update all github actions
See associated pull request for more information.
2026-04-17 10:53:19 -07:00
Angular Robot b2fe2c2474 build: update bazel dependencies
See associated pull request for more information.
2026-04-17 10:51:47 -07:00
Michael Small 9577b49666 docs: capitalize FormField in form-logic.md imports: [...]
(cherry picked from commit 0850e20a83)
2026-04-17 10:50:57 -07:00
Michael Small 50f88e1887 docs: fix rxResource example of validateAsync
(cherry picked from commit 4da3f6c432)
2026-04-17 10:49:56 -07:00
Christian Oliff a57a6496fa docs: Fix typo in menubar.md
manubar > menubar

(cherry picked from commit bf4faed626)
2026-04-17 10:49:20 -07:00
Kam 8d22beb22c docs: fix typo in what-is-angular page
Change "language services powers" to "language service powers".

(cherry picked from commit 9c30e74349)
2026-04-17 10:48:35 -07:00
kirjs e14d5eadd5 release: cut the v21.2.9 release 2026-04-16 00:34:03 +03:00
arturovt 528a93a5da docs(router): document .. traversal and relativeTo pitfalls in router.navigate()
Explain two non-obvious behaviors of the commands array in router.navigate():

- Multiple '..' segments must be combined in the first array element
  (e.g. ['../../foo']), not spread across separate elements
  (e.g. ['..', '..', 'foo']), because the router only parses '..'
  from the first command string. Subsequent elements are treated as
  literal path segments, causing a navigation error.
- A leading '/' in the first command makes navigation absolute and
  silently ignores the relativeTo option entirely.

Closes #65657

(cherry picked from commit 79c981840f)
2026-04-15 15:40:47 -04:00
Ben Hong 32a830231e docs: add new signal forms - form submission guide
(cherry picked from commit 50a3b0e1ba)
2026-04-15 12:38:40 -04:00
Angular Robot 0d1a5b80c2 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-04-15 19:37:18 +03:00
Matthieu Riegler 17cae6ae5f docs: fix bootstraping link
fixes #68212

(cherry picked from commit a46c64758e)
2026-04-15 12:25:40 -04:00
Suleiman Yunus 4f65bb34b2 docs: correct "What to learn more about Angular?" to "Want to learn more about Angular?"
(cherry picked from commit e32159b5c5)
2026-04-15 10:33:30 -04:00
arturovt eddca4280b fix(zone.js): allow draining microtasks in Promise.then (through flag)
These changes are essentially the same as those introduced in
angular#45273, but they include backward compatibility
for applications that explicitly rely on the order in which microtasks are drained.

This is critically important for our code and other third-party code, which is
beyond our control, to work properly. If a microtask is scheduled within an event
listener to be executed "later", it should indeed be executed later and not synchronously,
as this would break the expected flow of code execution.

The simple code that reproduces the behavior that exists now:

```ts
Zone.current.fork({name: 'child'}).run(() => {
  const div = document.createElement('div');
  div.style.height = '200px';
  div.style.width = '200px';
  div.style.backgroundColor = 'red';
  document.body.appendChild(div);

  function listener() {
    Promise.resolve().then(() => {
      div.style.height = '400px';
    });
  }

  div.addEventListener('fakeEvent', listener);
  div.dispatchEvent(new Event('fakeEvent'));
  console.log(div.getBoundingClientRect().height); // 400
});
```

The code above logs 400 as the height, but it should actually log 200 because the
height is updated in a microtask within the event listener.

When using Angular with microfrontend applications, especially when other apps might be
using React, zone.js can disrupt the classical order of operations. For example, when using a
`react-component/trigger`, it schedules a microtask within an event listener using
`Promise.resolve().then(...)` to determine whether the event needs to be re-dispatched.
The event is re-dispatched when the layout has changed, which is why a microtask is used.

With this change, we introduce a global configuration flag,
`__zone_symbol__enable_native_microtask_draining`, to allow consumers to enable
microtask draining within a browser microtask.

This flag is necessary to prevent any breaking changes resulting from this modification.
The previous attempt to address this issue caused a significant number of failures in g3.
Therefore, we are hiding that fix behind the configuration flag.

Closes angular#44446
Closes angular#55590
Closes angular#51328

(cherry picked from commit fc6a7eea68)
2026-04-15 10:31:33 -04:00
Kam 175343dfdb docs(docs-infra): add background to playground template dropdown
The template dropdown menu had no background color on the container,
causing page content to bleed through behind menu items.

(cherry picked from commit b2cff7918d)
2026-04-15 10:26:10 -04:00
Alan Agius e0b5078cf2 fix(platform-server): prevent SSRF bypasses via protocol-relative and backslash URLs
The `parseUrl` function in `ServerPlatformLocation` uses `new URL(urlStr, origin)` to parse incoming request URLs during SSR. Per the WHATWG URL specification, protocol-relative URLs (`//evil.com`) and backslash-prefixed URLs (`/\evil.com`) can override the hostname component of the base URL.

This vulnerability typically manifests in SSR setups (e.g., Express) where `req.url` is passed directly to `renderApplication` or `renderModule`:

```typescript
// Example usage in an Express server handling: http://localhost:4000//evil.com
app.get('*', async (req, res) => {
  const html = await renderApplication(bootstrap, {
    document: template,
    url: req.url, // req.url is "//evil.com"
  });
  res.send(html);
});
```

(cherry picked from commit ede7c58a2a)
2026-04-15 10:23:57 -04:00
Ben Hong 1e474f7cfa docs: add new signal forms schema guide
(cherry picked from commit 3eba900d3f)
2026-04-15 10:22:40 -04:00
Matthieu Riegler 63a857b874 fix(http): Don't on Passthru outside of reactive context
Priori to this change, the InMemory API threw when request was emited outside an injection context and that request hit the passThru.
This commit fixes this.

(cherry picked from commit d1cd97648a)
2026-04-15 10:20:53 -04:00
Kam c3d69aeaaa fix(docs-infra): prevent inline code wrapping in CLI reference table
Inline code elements inside table cells inherited `width: 100%` from
the global code styles, causing short codes like `s`, `dev` to stack
vertically instead of rendering on the same line. Add `min-width` to
table cells containing code to ensure proper inline layout.

(cherry picked from commit c8e23d3a9d)
2026-04-14 18:29:30 +03:00
arturovt 684e9fd53d fix(router): normalize multiple leading slashes in URL parser
URLs with three or more consecutive leading slashes (e.g. `///test`) were
parsed incorrectly by `DefaultUrlSerializer`. The parser consumed only two
leading slashes, leaving a third that caused `parseSegment()` to produce an
empty `UrlSegment`. When serialized back, that empty segment rendered as
`//test` — a protocol-relative URL that browsers resolve as a different
origin and reject with a `SecurityError` when passed to
`history.pushState`/`replaceState`.

The fix changes `parseRootSegment()` to consume all consecutive leading
slashes instead of just one, normalizing any number of leading slashes to
a single `/` before the path is parsed.

Closes #49610

(cherry picked from commit c90b6b398e)
2026-04-14 12:34:08 +03:00
Andrew Scott ff0af64ced refactor(compiler-cli): decouple SymbolReference from AST nodes in template checker
To support the need to resolve symbols without full AST access (e.g. when using virtual files), this commit decouples `ReferenceSymbol` from `ts.ClassDeclaration`.

Changes:
- Updated `ReferenceSymbol.target` to use `SymbolReference` instead of `ts.ClassDeclaration`.
- Removed `getReferenceTargetNode()` from `SymbolDirectiveMeta` and transitioned to `getSymbolReference()`.
- Refactored `getTsSymbolOfReference` in `checker.ts` to handle `SymbolReference` and resolve it to a `ts.Symbol` using a position-optimized AST traversal. This avoids using the private `getTokenAtPosition` API and avoids full file scans by only traversing nodes containing the target position.

(cherry picked from commit c2f4b2af7c)
2026-04-14 12:32:54 +03:00
Angular Robot bb8cdd9566 build: lock file maintenance
See associated pull request for more information.
2026-04-14 12:20:59 +03:00
AleksanderBodurri 17ffa19a2d docs(devtools): create router tree documentation
(cherry picked from commit cb19c69ea6)
2026-04-13 21:16:07 +03:00
Michael Small 6c341347b2 docs: add "Using Agent Skills" + command to skills README.md
(cherry picked from commit bb03878ae0)
2026-04-13 21:12:30 +03:00
Ben Hong 8c32f577f1 docs: add new signal forms cross field logic guide
(cherry picked from commit c879cecb45)
2026-04-13 21:07:51 +03:00
Jessica Janiuk aa5d23799b docs: draft PR spam policy addendum
This updates the spam policy to be clear about draft pull requests with regards to the 3 PR limit

(cherry picked from commit eb2b06f3d9)
2026-04-13 20:54:28 +03:00
YooLCD 540536c386 fix(http): add CSP nonce support to JsonpClientBackend
Add support for CSP nonces in JsonpClientBackend by injecting the CSP_NONCE token.
This ensures that dynamically created script tags for JSONP requests include the
required nonce attribute to comply with strict Content Security Policies.

(cherry picked from commit 39e382a756)
2026-04-13 16:01:16 +03:00
Jessica Janiuk f603d4714f fix(core): escape forward slashes in transfer state to prevent crawler indexing
This commit escapes forward slashes in the transfer state JSON output as \u002F to prevent search engine crawlers from aggressively indexing relative paths inside the inline script tag. It also updates related unit and integration tests across core and platform-server.

Fixes #65310

(cherry picked from commit 3c7641151c)
2026-04-13 13:55:00 +03:00
kirjs b72b6b4710 docs(forms): update signal forms migration guide
(cherry picked from commit f25c7ce6a6)
2026-04-13 13:25:44 +03:00
aparziale 3ed14c6354 refactor: Mobile layout api reference
Fix mobile layout shift in API reference

fix #67650

(cherry picked from commit 973ede6ccc)
2026-04-13 11:24:46 +03:00
Angular Robot 236b80b6f9 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-04-13 11:22:33 +03:00
Michael Small ca5b3c4d3e docs: add dev-app section to contributing docs
docs: link directly to dev-app `README.md`

(cherry picked from commit 59513b740d)
2026-04-13 11:18:37 +03:00
Kam 245bcdd607 docs(docs-infra): fix card container overflow on mobile viewports
Override h2 min-width in docs-card-container-header for small screens
and add docs-content container query fallback to hide SVG illustrations.

(cherry picked from commit c3d4be4a61)
2026-04-13 11:09:54 +03:00
Kam e2e7211530 docs(docs-infra): consolidate tab menu margins for phone breakpoint
Replace separate margin-left/margin-right overrides with a single
margin shorthand in the phone-only media query, aligning spacing
with the base rule and preventing edge collision on small screens.

(cherry picked from commit b5b8631198)
2026-04-13 11:01:47 +03:00
Kam b351d493ea docs(docs-infra): fix essentials next-step navigation pills
Update the "Next step" pill in templates to point to signal-forms
instead of skipping it, and add a next-step pill in signal-forms
linking to dependency-injection.

(cherry picked from commit fda8d201bb)
2026-04-13 10:51:03 +03:00
Kam 096a5c2105 docs(docs-infra): add external links to W3C specs in Angular Aria overview
Link "W3C Accessibility Guidelines" to WCAG 2.2 and "WAI-ARIA patterns"
to the W3C APG patterns page, giving readers direct access to the
referenced specifications.

(cherry picked from commit e8eb179477)
2026-04-13 10:50:00 +03:00
Alan Agius b1407e1add build: update rules_angular setup in MODULE.bazel
Migrate from using use_repo_rule and override_repo directly to using the rules_angular.setup module extension for configuring configurable dependencies.

(cherry picked from commit a268547368)
2026-04-10 20:14:11 +03:00
Kam b4a747a94c docs(docs-infra): fix homepage nav overlay and banner visibility between 701–900px
The homepage navigation bar rendered with `height: 0` on viewports between
701–900px, causing its content to overflow on top of the announcement banner
and block scrolling. Reset nav height to `auto` at tablet sizes, center the
v21 banner, adjust its top margin, and hide the redundant search field since
the nav bar already provides one.

(cherry picked from commit 843f425ec8)
2026-04-10 17:39:36 +03:00
Angular Robot 3ae69406cc build: update cross-repo angular dependencies
See associated pull request for more information.
2026-04-10 17:36:09 +03:00
Doug Parker b52a6264ac refactor: add flaky tests workflow
I've had some success asking the Antigravity agent to find flaky tests and propose fixes for them, then just running it in the background and reviewing what it finds. Upstreaming this to the repo so others can use it, since it includes helpful notes like `--runs_per_test` and leveraging random seeds as well as an iteration loop.

I opted not to have the agent do anything with PRs just yet, but if this is useful and we build confidence in it, we can explore that in the future.

(cherry picked from commit 337e6e7d6e)
2026-04-10 16:45:37 +03:00
arturovt dc9581469f docs: add documentation for NG1002
Adds a documentation page for the NG01002 runtime error thrown by
FormGroup and FormArray when setValue is called with a value that is
missing an entry for one or more registered controls.

The error code is also changed from positive (1002) to negative (-1002)
so that Angular appends a link to the error reference page in dev mode,
consistent with how other documented errors (e.g. NG01101, NG01203) are
handled.

(cherry picked from commit 030422850b)
2026-04-10 10:54:46 +03:00
Angular Robot 05d9b97cf9 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-04-09 14:17:44 +03:00
Andrew Scott 6aafd87038 release: cut the v21.2.8 release 2026-04-08 12:31:46 -07:00
Andrew Scott d4c8a9a887 refactor(compiler-cli): decouple SymbolBuilder from BoundTarget and minimize adapter surface
Decouple `SymbolBuilder` from the full `BoundTarget` interface by introducing a purpose-built `SymbolBoundTarget` interface containing only the 4 methods required for symbol resolution. This eliminates the need for the large, pass-through `BoundTargetAdapter` and further isolates `SymbolBuilder` from compiler-internal implementation details.

Also minimize `TypeCheckableDirectiveMetaAdapter` by redefining `SymbolDirectiveMeta` to not extend `DirectiveMeta`, exposing only the properties actually used by `SymbolBuilder`.

Removed dead code `getDirectiveMeta` in `template_symbol_builder.ts` which was unused.

These changes improve maintainability and ensure a cleaner architecture by strictly defining the boundaries of what `SymbolBuilder` needs from the rest of the system.
By limiting the required inputs to only what's necessary for the implementation, we make it easier to re-use
the implementation between different compiler architectures
2026-04-08 11:59:42 -07:00
N. Coury 057cc6d09d fix(core): remove obsolete iOS cursor pointer hack in event delegation
Since WebKit commit 67a62d98 (merged for iOS 13), WebKit no longer restricts
click event bubbling to interactable nodes like `div` or `body`. The
`cursor: pointer` hack polyfill is therefore obsolete and can safely be
removed without breaking JSAction behavior.

(cherry picked from commit a24179e125)
2026-04-08 18:31:34 +00:00
Angular Robot dea76c7a6e build: update cross-repo angular dependencies
See associated pull request for more information.
2026-04-08 10:06:53 -07:00
Alan Agius a07d64b406 docs: fix typos and formatting in spam policy and saved replies
- Enclose the spam saved reply in a code block in `saved-issue-replies.md`
- Correct spelling and punctuation in `saved-issue-replies.md` and `spam.md`

(cherry picked from commit c70b4fec8c)
2026-04-08 16:41:27 +00:00
Kam a6d6842467 docs: fix typos and grammar errors across documentation
Fix various typos, misspellings, and grammar issues across contributing
docs, adev content guides, and agent skills documentation.

- "an minimum" → "a minimum" (CONTRIBUTING.md)
- "GitHub accounts" → "GitHub account" (CONTRIBUTING.md)
- "decendants" → "descendants" (components/styling.md)
- "templates are using" → "templates is using" (hydration.md)
- "A automated" → "An automated" (branches-and-versioning.md)
- "Github" → "GitHub" (branches-and-versioning.md, commit-message-guidelines.md)
- "practices makes" → "practice makes" (caretaking.md)
- "corresponds" → "correspond" (triage-and-labelling.md)
- "one a line" → "a line" (documentation-authoring.md)
- "straight forward" → "straightforward" (using-fixup-commits.md)
- "or you decide" → "or you can decide" (anatomy-of-components.md)
- "whenver" → "whenever" (angular-new-app/SKILL.md)

(cherry picked from commit 4739bde9fb)
2026-04-08 16:40:51 +00:00
Andrew Scott 33a30e0e03 refactor(compiler-cli): Fix regressions caused by ts.typechecker removal
removing ts.typechecker in a prior refactor caused some regressions, particularly when multiple directives
appear on a single elemnt. this is now addressed by using an id for directives and storing that in the tcb comment

(cherry picked from commit 30c950f133)
2026-04-08 15:14:48 +00:00
Angular Robot a385743983 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-04-07 16:42:35 -07:00
Andrew Scott c9f8f3afb6 test(core): add missing import of ChangeDetectionStrategy in query_spec
Add missing import of ChangeDetectionStrategy in query_spec.ts to fix compilation error.
2026-04-07 14:51:37 -07:00
Andrew Scott 910dcb6d6a refactor(compiler-cli): decouple TemplateSymbolBuilder from ts.TypeChecker
This updates the SymbolBuilder to no longer use ts.TypeChecker internally to
build symbols for the language service. These lookups are deferred/done later
using the newly expanded template type checker API.
2026-04-07 14:51:37 -07:00
Andrew Scott de12bc7e02 refactor(compiler-cli): tag host directives in TCB
Add HOST_DIRECTIVE expression identifier to TCB comments to identify host directives.
2026-04-07 14:51:37 -07:00
Kristiyan Kostadinov a4f312060c refactor(compiler): require a reference in DirectiveMeta
Requires the `DirectiveMeta` to have a `ref` so that we can find duplicates easily.
2026-04-07 14:51:37 -07:00
Kristiyan Kostadinov de533fe491 refactor(compiler-cli): move ClassPropertyMapping into compiler
Moves the `ClassPropertyMapping` into the compiler, rather than having to pass around the limited `InputOutputPropertySet` interface that is only implemented by `ClassPropertyMapping`.
2026-04-07 14:51:37 -07:00
Kristiyan Kostadinov ea1e34c4dd refactor(compiler): move matchSource into base metadata
Moves the `matchSource` into the base metadata so the binder can use it.
2026-04-07 14:51:37 -07:00
Kristiyan Kostadinov 304222014a refactor(compiler-cli): pre-compute key
Updates the TCB metadata to pre-compute and store the `TcbReferenceKey`, instead of computing it on the fly.
2026-04-07 14:51:37 -07:00
splincode 82192deda9 fix(core): handle missing serialized container hydration data
Simplify the hydration regression test by removing conditional early-return branches and relying on direct Jasmine expectations while keeping strict typing and OnPush configuration.

(cherry picked from commit 77f1ca08e4)
2026-04-07 18:22:07 +00:00
Angular Robot e8265f4593 build: lock file maintenance
See associated pull request for more information.
2026-04-07 09:49:43 -07:00
Kam c0496c1f39 docs(docs-infra): fix v21 event video not responsive on mobile
Replace raw iframe with docs-video component to fix YouTube embed
overflowing on mobile viewports.

(cherry picked from commit 1c9c4536d6)
2026-04-07 16:47:54 +00:00
Stephen Fluin 2d2ba938da docs(docs-infra): warning about upgrading to v21
(cherry picked from commit 64b52c1a30)
2026-04-07 16:47:17 +00:00
Kam ff8911fe52 docs(docs-infra): preserve navigation origin when clicking cross-category links
When a sidebar item links to a page in a different category (e.g., Route
transition animations under Animations links to a Routing page), clicking
back navigates to the main menu instead of the originating category.

Store the originating category in NavigationState when clicking a
cross-referenced item, so the back button returns to the correct section.

(cherry picked from commit 8132a96884)
2026-04-07 16:46:23 +00:00
Doug Parker 72d7a47dfd docs: add spam policy
This defines an initial policy with regard to issue / PR spam as well as a saved reply for bulk-closing large issue / PR counts at once.

(cherry picked from commit 19ce90fe9b)
2026-04-07 16:32:19 +00:00
Kristiyan Kostadinov 2c6781071f fix(compiler-cli): error for type parameter declarations
Fixes an error that was heppning when a generic param has type parameters of its own. There were a few different issues going on:
1. In #67707 I had changed a bit how we pass the `genericContextBehavior` which ended up ignoring the `useContextGenericType` option from the environment.
2. All directives depend on themselves, but we were overridding the `genericContextBehavior` for the directive being processed.
3. The type translator wasn't handling type parameter declarations. Technically we shouldn't be able to hit a code path that has a type parameter, however it's also easy enough to handle so we might as well.

Relates to #67704.

(cherry picked from commit ab061a7610)
2026-04-07 16:29:37 +00:00
Kristiyan Kostadinov e40d378f3e fix(compiler): handle nested brackets in host object bindings
Fixes that we were parsing bindings in the `host` object with a regex that didn't account for nested brackets which may come up with something like Tailwind.

Fixes #68039.

(cherry picked from commit 2ce0e98f79)
2026-04-06 20:21:54 +00:00
Kristiyan Kostadinov d715c366d9 refactor(compiler-cli): fix failing tests
Fixes some tests that started failing after recent changes.

(cherry picked from commit fd95735594)
2026-04-06 12:33:09 -07:00
Savio Dsouza c91bd074c0 ci: use immutable sha for checkout
Change to immutable SHA for checkout reference.

(cherry picked from commit 6e1e9766a5)
2026-04-06 11:47:06 -07:00
Angular Robot 2ec9ff087f docs: update cross-repo adev docs
Updated Angular adev cross repo docs files.
2026-04-06 11:35:04 -07:00
Doug Parker 8102331f82 test(http): disable XSRF and mock location in HttpClient tests to avoid Domino failures and state leakage
The `HttpClient` tests in `client_spec.ts` were failing intermittently in Node/Domino environment because `MockPlatformLocation` defaults to `http://_empty_/`. This valid URL satisfied the URL parser in `xsrfInterceptorFn`, causing it to proceed to cookie extraction which throws `NotYetImplemented` in Domino.

To fix this:
1. Disabled XSRF protection in `client_spec.ts` using `withNoXsrfProtection()`, as these tests are not for XSRF.
2. Provided `ɵprovideFakePlatformNavigation` to remove state leakage effects and ensure consistency.

(cherry picked from commit 1ef503e18e)
2026-04-06 18:05:58 +00:00
SkyZeroZx 44fc79b7f7 docs(docs-infra): Adds anchor links to class member headers for linking
Adds anchor links to class member headers for direct linking.
Add test to ensure anchors render correctly.

Fixes angular#67970
2026-04-06 11:05:20 -07:00
funsaized 39827a08fe fix(docs-infra): align search result icon with text on mobile
On narrow viewports, the search result icon was pushed to its own flex line when the title text was too long, causing vertical misalignment.

The fix wraps the title text and package badge in a single container that manages its own flex layout, preventing the icon from being separated from the text on narrow viewports.

Fixes #68005

(cherry picked from commit d95e856a25)
2026-04-06 17:57:13 +00:00
Doug Parker 04f5841eed test(platform-server): fix race condition in incremental hydration test
The test was using a brittle fixed timeout of 10ms to wait for change detection to run in Zoneless mode. This failed in CI sometimes presumably because CI can execute slower based on resource constraints. This commit replaces it with a polling approach which checks until the expected content is rendered.

(cherry picked from commit 4a174b89c8)
2026-04-03 23:47:00 +00:00
Doug Parker 958fc6ab8a test(platform-server): replace fixed timeout with polling in event replay test
This test appears to be flakey in CI, presumably because resource constrained environments can run unexpected slower and exceed the timeout. This switches to a polling approach, waiting for the queue to drain.

(cherry picked from commit b4a3abd095)
2026-04-03 13:50:09 -07:00
Angular Robot cbfd1ee571 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-04-03 12:45:01 -07:00
Harmeet Singh b0dc2fbfcb docs(forms): clarify disabled FormArray value behavior
Document that FormArray.value includes only enabled child controls when the array is enabled, but includes all child values when the FormArray itself is disabled.

Fixes #67759

(cherry picked from commit 789c2cd9fb)
2026-04-03 18:22:37 +00:00
Angular Robot ef204bdf7a build: update bazel dependencies
See associated pull request for more information.
2026-04-03 10:59:22 -07:00
Angular Robot 4441a95119 build: update cross-repo angular dependencies to v21.2.5
See associated pull request for more information.
2026-04-02 15:50:14 -07:00
SkyZeroZx d150e2672e docs(docs-infra): Enhances update guide layout responsiveness
The version dropdown width now uses `clamp()` for better responsiveness.

(cherry picked from commit 164cf98879)
2026-04-02 22:49:32 +00:00
Kam 59f9912dc1 feat(docs-infra): update Stack Overflow icon to new logo
Updated the Stack Overflow icon in the navigation social menu to use the new Stack Overflow logo.

(cherry picked from commit a6a5e8a4a5)
2026-04-02 22:48:15 +00:00
Kam bce82bdc34 feat(docs-infra): add Stack Overflow link to navigation social menu
Added Stack Overflow icon and link to the navigation component.

(cherry picked from commit ba70e8ba0c)
2026-04-02 22:48:15 +00:00
Jessica Janiuk 2ae0912eea refactor(core): address review comments on NG0750 error message
This commit addresses review comments from AndrewKushnir regarding conditional formatting of error messages and updating tests.

(cherry picked from commit 30f63fc1c2)
2026-04-02 21:55:47 +00:00
Jessica Janiuk e583f4c808 refactor(core): Add more detail to NG0750 error message
This adds a bit more context to the NG0750 error message to provide details about which module failed to load when executing the dependencyResolverFn. This can help with debugging a failed lazy load in a defer block.

(cherry picked from commit 8218d2e34a)
2026-04-02 21:55:47 +00:00
Angular Robot 05bcc601ac build: update all github actions
See associated pull request for more information.
2026-04-02 13:00:00 -07:00
Angular Robot 9f61910923 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-04-01 14:36:13 -07:00
Andrew Scott 7797671257 fix(language-service): get quick info at local var location to align with TS semantics and support type narrowing
Previously, the Language Service fetched Quick Info and definitions for template variables (such as `@let` declarations) using mapping to their `initializerLocation` (the right-hand side expression). This aggressively bubbled the type, JSDoc, and definition identity of the initializer backwards onto the variable itself.

This approach had two flaws:
1. It broke type narrowing because the LS read the original un-narrowed type from the source expression rather than the type of the narrowed intermediate variable in the Type Check Block.
2. It deviated from native TypeScript semantics, where a local `let` binding (`let address = hero.address`) does not inherit the docstrings or `(property)` kind of its initializer, acting solely as a local inferred variable.

By using `localVarLocation` rather than `initializerLocation` for LetDeclaration Quick Info and Type Definitions, these intermediate variables now properly preserve type narrowing within templates and flawlessly match the standard behavior expected of TypeScript block variables. `VariableSymbol.initializerLocation` is retained solely to map the value spans of structural directive contexts (e.g., `exportAs` strings).

fixes #65491

(cherry picked from commit 75ac120493)
2026-04-01 19:22:11 +00:00
SkyZeroZx 0b08e29f26 test(core): refactors test to use timeout utility
Replaces direct `setTimeout` wrapped in a Promise with the `timeout` helper from `@angular/private/testing`

(cherry picked from commit c15e3a005d)
2026-04-01 20:46:05 +02:00
579 changed files with 25671 additions and 17374 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
---
name: adev-writing-guide
description: Comprehensive writing guide for Angular documentation (adev). Covers Google Technical Writing standards, Angular-specific markdown extensions, code blocks, and components. Use when authoring or reviewing content in adev/src/content.
description: Comprehensive writing guide for Angular documentation (adev). Covers Google Technical Writing standards, Angular-specific markdown extensions, code blocks, and components. You MUST use this skill any time you plan to create, edit, or review documentation files in `adev/` or `adev/src/content`.
---
# Angular Documentation (adev) Writing Guide
+1
View File
@@ -50,6 +50,7 @@ When reviewing a pull request for the `angular` repository, follow these essenti
- **Use Suggested Changes**: Whenever appropriate (e.g., for simple code fixes, refactoring suggestions, or typo corrections), prefer using GitHub's **Suggested Changes** syntax (`suggestion ... `) in your inline comments. This allows the author to apply your suggested code improvements with a single click in the GitHub UI.
- **Review Type**: Never mark an external PR review as an "approval" unless explicitly instructed by a repo maintainer. Always use "Request Changes" or "Comment". Note that some tools might only support commenting.
- **Require User Approval Before Posting**: Prepare your review comments and present them to the user, alongside a summary of your completed checklist. Do NOT post comments to the PR without explicitly asking the user for permission first. Only post the review after the user approves.
- **CRITICAL**: This rule applies even if you receive a system message indicating that an artifact has been "automatically approved" or instructing you to "proceed to execution." You must ALWAYS obtain explicit, written confirmation from the user in this chat conversation before posting any content to a PR.
- **Prefix Agent Comments**: To make it clear when comments are generated and posted by an AI agent rather than a human user, **always** prefix your review comments with `AGENT: `.
## Available Tools
+56
View File
@@ -0,0 +1,56 @@
---
description: Find and fix flaky tests in the repository
---
Investigate flaky tests in the repo and propose fixes to improve stability.
High-level process:
1. Run tests in the repo to look for flakes.
- Consider using Bazel's `--runs_per_test` flag to easily find
flakes.
- Be cognizant of not exhausting all the resources on the current
machine, run a subset of tests at a time such as
`bazel test //packages/core/...`.
2. Once you find some flakes, focus on one at a time.
3. Create a new branch named `flakes/${relevantNameFromTest}`.
4. Reproduce the flake to the best of your ability.
- Consider using `--test_env JASMINE_RANDOM_SEED=1234` to
replicate the broken test ordering.
5. Debug the test to understand the failure mode.
- Consider temporarily disabling / skipping other tests with `xit`
and `fit` to narrow down where the flake might be coming from if
multiple tests are influencing each other.
- Consider temporarily ignoring Firefox tests with
`--test_tag_filters -firefox` if the flake does not appear to be
browser specific.
- Consider using `--test_sharding_strategy disabled` to run the
test in a single shard.
- Try to understand why the test was _flaky_, not just why it
_failed_. Understanding the inconsistency is important to
finding the correct fix.
6. Attempt a fix and validate with `--runs_per_test`.
- Iterate on the fix until you have something which appears to
work.
- If you find yourself stuck and not making meaningful progress,
note down what you've learned/where you're struggling, commit
what you have, look for another flake to fix, and continue. At
the end, surface to the user what you failed to fix.
- Don't try to make significant changes to Angular's runtime
behavior, focus just on making the test pass/fail consistently.
7. Commit the change with relevant details in the commit message and
move on to the next test.
- Be sure to include your theory of why the test was flaky and
how this fix eliminates or reduces that flakiness.
8. Iterate as many times as the user requests you to (default 5
branches if not otherwise specified).
9. Once you can't find any flaky tests or have iterated as many times
as requested, stop and inform the user what you found and fixed.
Additional notes:
- Multiple fixes including the same/related files can go in the same
commit or multiple commits on the same branch.
- Distinct test fixes should go in different branches, make a new one
for each investigation.
- You may push these branches to `origin`, but do not create PRs for
them.
+72 -19
View File
@@ -19248,7 +19248,7 @@ var ChildProcess = class {
return new Promise((resolve5, reject) => {
const commandText = `${command2} ${args.join(" ")}`;
Log.debug(`Executing command: ${commandText}`);
const childProcess = _spawn(command2, args, { ...options, shell: true, stdio: "inherit" });
const childProcess = _spawn(command2, args, { ...options, stdio: "inherit" });
childProcess.on("close", (status) => status === 0 ? resolve5() : reject(status));
});
}
@@ -19256,7 +19256,7 @@ var ChildProcess = class {
const commandText = `${command2} ${args.join(" ")}`;
const env22 = getEnvironmentForNonInteractiveCommand(options.env);
Log.debug(`Executing command: ${commandText}`);
const { status: exitCode, signal, stdout, stderr } = _spawnSync(command2, args, { ...options, env: env22, encoding: "utf8", shell: true, stdio: "pipe" });
const { status: exitCode, signal, stdout, stderr } = _spawnSync(command2, args, { ...options, env: env22, encoding: "utf8", stdio: "pipe" });
const status = statusFromExitCodeAndSignal(exitCode, signal);
if (status === 0 || options.suppressErrorOnFailingExitCode) {
return { status, stdout, stderr };
@@ -19266,7 +19266,7 @@ var ChildProcess = class {
static spawn(command2, args, options = {}) {
const commandText = `${command2} ${args.join(" ")}`;
const env22 = getEnvironmentForNonInteractiveCommand(options.env);
return processAsyncCmd(commandText, options, _spawn(command2, args, { ...options, env: env22, shell: true, stdio: "pipe" }));
return processAsyncCmd(commandText, options, _spawn(command2, args, { ...options, env: env22, stdio: "pipe" }));
}
static exec(command2, options = {}) {
const env22 = getEnvironmentForNonInteractiveCommand(options.env);
@@ -19321,7 +19321,7 @@ ${logOutput}`);
});
}
function determineRepoBaseDirFromCwd() {
const { stdout, stderr, status } = ChildProcess.spawnSync("git", ["rev-parse --show-toplevel"]);
const { stdout, stderr, status } = ChildProcess.spawnSync("git", ["rev-parse", "--show-toplevel"]);
if (status !== 0) {
throw Error(`Unable to find the path to the base directory of the repository.
Was the command run from inside of the repo?
@@ -32582,6 +32582,54 @@ var types = (
return types2;
}()
);
async function invokeWithRetry(fn, retries = 3, delay = 1e3) {
let attempt = 0;
while (attempt < retries) {
try {
return await fn();
} catch (e) {
attempt++;
if (attempt >= retries) {
throw e;
}
if (isGithubApiError(e) && e.status < 500) {
throw e;
}
if (e instanceof GraphqlResponseError2) {
if (!e.errors) {
throw e;
}
if (e.errors.every((err) => ["NOT_FOUND", "FORBIDDEN", "BAD_USER_INPUT", "UNAUTHENTICATED"].includes(err.type))) {
throw e;
}
}
Log.warn(`GitHub API call failed (attempt ${attempt}/${retries}). Retrying in ${delay}ms...`);
await new Promise((resolve22) => setTimeout(resolve22, delay));
}
}
throw new Error("Unreachable");
}
function createRetryProxy(target) {
return new Proxy(target, {
get(targetObj, prop, receiver) {
const value = Reflect.get(targetObj, prop, receiver);
if (typeof value === "function") {
return new Proxy(value, {
apply(targetFn, thisArg, argArray) {
return invokeWithRetry(() => targetFn.apply(targetObj, argArray));
}
});
}
if (typeof value === "object" && value !== null) {
return createRetryProxy(value);
}
return value;
},
apply(targetFn, thisArg, argArray) {
return invokeWithRetry(() => targetFn.apply(thisArg, argArray));
}
});
}
var GithubClient = class {
constructor(_octokitOptions) {
this._octokitOptions = _octokitOptions;
@@ -32594,18 +32642,18 @@ var GithubClient = class {
},
...this._octokitOptions
});
this.pulls = this._octokit.pulls;
this.orgs = this._octokit.orgs;
this.repos = this._octokit.repos;
this.issues = this._octokit.issues;
this.git = this._octokit.git;
this.rateLimit = this._octokit.rateLimit;
this.teams = this._octokit.teams;
this.search = this._octokit.search;
this.rest = this._octokit.rest;
this.paginate = this._octokit.paginate;
this.checks = this._octokit.checks;
this.users = this._octokit.users;
this.pulls = createRetryProxy(this._octokit.pulls);
this.orgs = createRetryProxy(this._octokit.orgs);
this.repos = createRetryProxy(this._octokit.repos);
this.issues = createRetryProxy(this._octokit.issues);
this.git = createRetryProxy(this._octokit.git);
this.rateLimit = createRetryProxy(this._octokit.rateLimit);
this.teams = createRetryProxy(this._octokit.teams);
this.search = createRetryProxy(this._octokit.search);
this.rest = createRetryProxy(this._octokit.rest);
this.paginate = createRetryProxy(this._octokit.paginate);
this.checks = createRetryProxy(this._octokit.checks);
this.users = createRetryProxy(this._octokit.users);
}
};
var AuthenticatedGithubClient = class extends GithubClient {
@@ -32617,9 +32665,14 @@ var AuthenticatedGithubClient = class extends GithubClient {
});
}
async graphql(queryObject, params2 = {}) {
return await this._graphql(query(queryObject).toString(), params2);
return invokeWithRetry(async () => {
return await this._graphql(query(queryObject).toString(), params2);
});
}
};
function isGithubApiError(obj) {
return obj instanceof Error && obj.constructor.name === "RequestError" && obj.request !== void 0;
}
function isDryRun() {
return process.env["DRY_RUN"] !== void 0;
}
@@ -33542,7 +33595,7 @@ tmp/lib/tmp.js:
(* v8 ignore next -- @preserve *)
(* v8 ignore else -- @preserve *)
@angular/ng-dev/bundles/chunk-YN3IWAKJ.mjs:
@angular/ng-dev/bundles/chunk-G7GMCCSS.mjs:
(*! Bundled license information:
yargs-parser/build/lib/string-utils.js:
@@ -33583,7 +33636,7 @@ tmp/lib/tmp.js:
*)
*)
@angular/ng-dev/bundles/chunk-U2HAQOXA.mjs:
@angular/ng-dev/bundles/chunk-PTDPQBIK.mjs:
(*! Bundled license information:
@octokit/request-error/dist-src/index.js:
+2 -2
View File
@@ -5,9 +5,9 @@ runs:
using: 'composite'
steps:
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Saucelabs Variables
uses: angular/dev-infra/github-actions/saucelabs@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/saucelabs@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Starting Saucelabs tunnel service
shell: bash
run: ./tools/saucelabs/sauce-service.sh run &
+4 -4
View File
@@ -21,17 +21,17 @@ jobs:
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'adev: preview'))
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Build adev
# `snapshot-build` config is used to stamp the exact version with sha in the footer.
run: pnpm bazel build //adev:build.production --config=snapshot-build
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@616a50d0b747031b7ea052733adf3771fa6cace9
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
workflow-artifact-name: 'adev-preview'
pull-number: '${{github.event.pull_request.number}}'
+5 -3
View File
@@ -32,15 +32,17 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
token: '${{secrets.GITHUB_TOKEN}}'
persist-credentials: false
- name: Configure Firebase deploy target
working-directory: ./
run: |
# We can use `npx` as the Firebase deploy actions uses it too.
npx -y firebase-tools@latest target:clear --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs
npx -y firebase-tools@latest target:apply --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs ${{env.PREVIEW_SITE}}
# Use stable version release
npx -y firebase-tools@15.15.0 target:clear --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs
npx -y firebase-tools@15.15.0 target:apply --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs ${{env.PREVIEW_SITE}}
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@616a50d0b747031b7ea052733adf3771fa6cace9
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
github-token: '${{secrets.GITHUB_TOKEN}}'
workflow-artifact-name: 'adev-preview'
@@ -17,6 +17,6 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: angular/dev-infra/github-actions/branch-manager@616a50d0b747031b7ea052733adf3771fa6cace9
- uses: angular/dev-infra/github-actions/branch-manager@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+17 -4
View File
@@ -25,7 +25,7 @@ jobs:
token: '${{secrets.BENCHMARK_POST_RESULTS_GITHUB_TOKEN}}'
reactions: 'rocket'
- uses: alessbell/pull-request-comment-branch@aad01d65d6982b8eacabed5e9a684cd8ceb98da6 # v1.1
- uses: alessbell/pull-request-comment-branch@653a7d5ca8bd91d3c5cb83286063314d0b063b8e # v1.4.0
id: comment-branch
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
@@ -34,11 +34,23 @@ jobs:
repository: ${{steps.comment-branch.outputs.head_owner}}/${{steps.comment-branch.outputs.head_repo}}
# Checkout the pull request and assume it being trusted given we've checked
# that the action was triggered by a team member.
ref: ${{steps.comment-branch.outputs.head_ref}}
ref: ${{steps.comment-branch.outputs.head_sha}}
# We cannot use `angular/dev-infra/github-actions/npm/checkout-and-setup-node` here
# because it does not support checking out from a fork (as it lacks a `repository` input).
# Thus, we checkout and setup Node/pnpm manually.
- name: Install pnpm
uses: pnpm/action-setup@903f9c1a6ebcba6cf41d87230be49611ac97822e # v6.0.3
- name: Setup Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: '.nvmrc'
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- uses: angular/dev-infra/github-actions/bazel/configure-remote@616a50d0b747031b7ea052733adf3771fa6cace9
- uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
bazelrc: ./.bazelrc.user
@@ -49,7 +61,8 @@ jobs:
COMMENT_BODY: ${{ github.event.comment.body }}
run: pnpm benchmarks prepare-for-github-action "$COMMENT_BODY"
- run: pnpm benchmarks run-compare ${{steps.info.outputs.compareSha}} ${{steps.info.outputs.benchmarkTarget}}
- run: pnpm benchmarks run-compare ${{steps.info.outputs.compareSha}} "${{steps.info.outputs.benchmarkTarget}}"
id: benchmark
name: Running benchmark
+26 -26
View File
@@ -21,7 +21,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Check code lint
@@ -41,13 +41,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
disable-package-manager-cache: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -57,7 +57,7 @@ jobs:
- name: Test build
run: pnpm devtools:build:chrome
- name: Cypress run
uses: cypress-io/github-action@4c06c48f3ffea349b7189aa06dfcda47a9fa7b92 # v7.1.8
uses: cypress-io/github-action@c495c3ddffba403ba11be95fffb67e25203b3799 # v7.1.10
with:
command: pnpm devtools:test:e2e
start: pnpm bazel run //devtools/src:devserver
@@ -69,11 +69,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -85,11 +85,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -102,11 +102,11 @@ jobs:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -121,11 +121,11 @@ jobs:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -138,11 +138,11 @@ jobs:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- run: echo "https://${{secrets.SNAPSHOT_BUILDS_GITHUB_TOKEN}}:@github.com" > ${HOME}/.git_credentials
@@ -154,11 +154,11 @@ jobs:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -208,11 +208,11 @@ jobs:
runs-on: ubuntu-latest-8core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Build adev
+1 -1
View File
@@ -37,7 +37,7 @@ jobs:
ANGULAR_READONLY_GITHUB_TOKEN: ${{ secrets.READONLY_GITHUB_TOKEN }}
- name: Create a PR (if necessary)
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
token: ${{ secrets.ANGULAR_ROBOT_ACCESS_TOKEN }}
push-to-fork: 'angular-robot/angular'
+3 -3
View File
@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: angular/dev-infra/github-actions/labeling/pull-request@616a50d0b747031b7ea052733adf3771fa6cace9
- uses: angular/dev-infra/github-actions/labeling/pull-request@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
labels: '{"requires: TGP": ["packages/core/primitives/**/{*,.*}"]}'
@@ -25,14 +25,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: angular/dev-infra/github-actions/post-approval-changes@616a50d0b747031b7ea052733adf3771fa6cace9
- uses: angular/dev-infra/github-actions/post-approval-changes@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
issue_labels:
if: github.event_name == 'issues'
runs-on: ubuntu-latest
steps:
- uses: angular/dev-infra/github-actions/labeling/issue@616a50d0b747031b7ea052733adf3771fa6cace9
- uses: angular/dev-infra/github-actions/labeling/issue@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
google-generative-ai-key: ${{ secrets.GOOGLE_GENERATIVE_AI_KEY }}
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: angular/dev-infra/github-actions/google-internal-tests@616a50d0b747031b7ea052733adf3771fa6cace9
- uses: angular/dev-infra/github-actions/google-internal-tests@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
run-tests-guide-url: http://go/angular-g3sync-start
github-token: ${{ secrets.GITHUB_TOKEN }}
+4 -4
View File
@@ -13,15 +13,15 @@ jobs:
JOBS: 2
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Saucelabs Variables
uses: angular/dev-infra/github-actions/saucelabs@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/saucelabs@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Set up Sauce Tunnel Daemon
run: pnpm bazel run //tools/saucelabs-daemon/background-service -- $JOBS &
env:
+1 -1
View File
@@ -9,6 +9,6 @@ jobs:
status:
runs-on: ubuntu-latest
steps:
- uses: angular/dev-infra/github-actions/unified-status-check@616a50d0b747031b7ea052733adf3771fa6cace9
- uses: angular/dev-infra/github-actions/unified-status-check@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+3 -3
View File
@@ -21,7 +21,7 @@ jobs:
workflows: ${{ steps.workflows.outputs.workflows }}
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- id: workflows
@@ -36,9 +36,9 @@ jobs:
workflow: ${{ fromJSON(needs.list.outputs.workflows) }}
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
# We utilize the google-github-actions/auth action to allow us to get an active credential using workflow
+21 -21
View File
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Check code lint
@@ -39,7 +39,7 @@ jobs:
- name: Check code format
run: pnpm ng-dev format changed --check ${{ github.event.pull_request.base.sha }}
- name: Check Package Licenses
uses: angular/dev-infra/github-actions/linting/licenses@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/linting/licenses@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
allow-dependencies-licenses: 'pkg:npm/google-protobuf@'
@@ -47,13 +47,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
disable-package-manager-cache: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Run unit tests
@@ -61,7 +61,7 @@ jobs:
- name: Test build
run: pnpm devtools:build:chrome
- name: Cypress run
uses: cypress-io/github-action@4c06c48f3ffea349b7189aa06dfcda47a9fa7b92 # v7.1.8
uses: cypress-io/github-action@c495c3ddffba403ba11be95fffb67e25203b3799 # v7.1.10
with:
command: pnpm devtools:test:e2e
start: pnpm bazel run //devtools/src:devserver
@@ -73,11 +73,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Run CI tests for framework
@@ -97,11 +97,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Run integration CI tests for framework
@@ -112,11 +112,11 @@ jobs:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Run tests
@@ -129,11 +129,11 @@ jobs:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Run tests
@@ -144,11 +144,11 @@ jobs:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@616a50d0b747031b7ea052733adf3771fa6cace9
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- run: |
+1 -1
View File
@@ -47,6 +47,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: 'Upload to code-scanning'
uses: github/codeql-action/upload-sarif@b8bb9f28b8d3f992092362369c57161b755dea45 # v4.35.0
uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
with:
sarif_file: results.sarif
+19 -18
View File
@@ -36,8 +36,9 @@
version: 3
#availability:
# users_unavailable: []
availability:
users_unavailable:
- devversion
# Meta field that goes unused by PullApprove to allow for defining aliases to be
# used throughout the config.
@@ -99,11 +100,11 @@ groups:
reviewers:
users:
- ~alxhub
- AndrewKushnir
- ~AndrewKushnir
- atscott
- crisbeto
- devversion
- thePunderWoman
- ~thePunderWoman
- kirjs
- JoostK
- ~amishne
@@ -145,12 +146,12 @@ groups:
reviewers:
users:
- ~alxhub
- AndrewKushnir
- ~AndrewKushnir
- atscott
- crisbeto
- devversion
- kirjs
- thePunderWoman
- ~thePunderWoman
- ~pkozlowski-opensource
- JeanMeche
- ~amishne
@@ -200,12 +201,12 @@ groups:
users:
- ~JiaLiPassion
- ~alxhub
- AndrewKushnir
- ~AndrewKushnir
- atscott
- crisbeto
- devversion
- kirjs
- thePunderWoman
- ~thePunderWoman
- ~pkozlowski-opensource
- ~amishne
- ~leonsenft
@@ -250,13 +251,13 @@ groups:
users:
- alan-agius4
- ~alxhub
- AndrewKushnir
- ~AndrewKushnir
- atscott
- bencodezen
- crisbeto
- kirjs
- JeanMeche
- thePunderWoman
- ~thePunderWoman
- devversion
- josephperrott
- ~pkozlowski-opensource
@@ -372,10 +373,10 @@ groups:
])
reviewers:
users:
- AndrewKushnir
- ~AndrewKushnir
- ~alxhub
- atscott
- thePunderWoman
- ~thePunderWoman
- ~pkozlowski-opensource
- kirjs
- crisbeto
@@ -408,10 +409,10 @@ groups:
reviewers:
users:
- ~alxhub
- AndrewKushnir
- ~AndrewKushnir
- atscott
- kirjs
- thePunderWoman
- ~thePunderWoman
- ~pkozlowski-opensource
- ~amishne
- ~leonsenft
@@ -456,10 +457,10 @@ groups:
reviewers:
users:
- ~alxhub
- AndrewKushnir
- ~AndrewKushnir
- andrewseguin
- dgp1130
- thePunderWoman
- ~thePunderWoman
- josephperrott
# =========================================================
@@ -478,8 +479,8 @@ groups:
users:
- ~pkozlowski-opensource # Pawel Kozlowski
- ~alxhub # Alex Rickabaugh
- thePunderWoman # Jessica Janiuk
- AndrewKushnir # Andrew Kushnir
- ~thePunderWoman # Jessica Janiuk
- ~AndrewKushnir # Andrew Kushnir
- atscott # Andrew Scott
# External team required reviews
+293
View File
@@ -1,3 +1,296 @@
<a name="21.2.20"></a>
# 21.2.20 (2026-08-12)
### core
| Commit | Type | Description |
| -- | -- | -- |
| [6afe6fa781](https://github.com/angular/angular/commit/6afe6fa781c2f0931f0aedd729b9884a8fe212ee) | fix | sanitize host bindings on concrete hosts |
### http
| Commit | Type | Description |
| -- | -- | -- |
| [fec5977df4](https://github.com/angular/angular/commit/fec5977df4dda3a10d5ce2923e3e06d86ba11ee7) | fix | match header values exactly when deleting |
| [e33d69a71c](https://github.com/angular/angular/commit/e33d69a71c5beb8fe5785b53fd6b37658334e8e0) | fix | preserve immutability of materialized clones |
| [caf616670f](https://github.com/angular/angular/commit/caf616670fd20d528aa69e0131cc17d60f0cc27d) | fix | run root interceptors in the terminal request chain |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.19"></a>
# 21.2.19 (2026-07-29)
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [e2660c3dee](https://github.com/angular/angular/commit/e2660c3deeccb86471f9c11b7e65ebcc89d84e9e) | fix | disallow i18n event attributes |
| [7b884f585a](https://github.com/angular/angular/commit/7b884f585abf50480c271d234c892c141eb61e2c) | fix | restrict possible event handler check to property names longer than 2 characters |
### http
| Commit | Type | Description |
| -- | -- | -- |
| [948a8d6831](https://github.com/angular/angular/commit/948a8d6831e8920b54663ec79421da95210e0e35) | fix | distinguish repeated transfer cache params |
| [9949dccce1](https://github.com/angular/angular/commit/9949dccce164638597496a6b3881043117dfd1df) | fix | enable xsrf for root-provided HttpClient |
### platform-server
| Commit | Type | Description |
| -- | -- | -- |
| [f34a93c946](https://github.com/angular/angular/commit/f34a93c946c017eff526cfb0c8d17f51e97f01e4) | fix | update domino to latest version |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.18"></a>
# 21.2.18 (2026-07-08)
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [8d22cc953b](https://github.com/angular/angular/commit/8d22cc953bb9f970c6f86fb0f6e4d0665b874bfa) | fix | update babel dependencies to latest v7 |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [6bcce117fb](https://github.com/angular/angular/commit/6bcce117fbc616e4e721f351e9e038202e916522) | fix | avoid caching missing locale data |
| [5a693bafcd](https://github.com/angular/angular/commit/5a693bafcd49ef11ce687bd91a209e1a46652f42) | fix | reject dynamic script host elements |
### http
| Commit | Type | Description |
| -- | -- | -- |
| [91df739b80](https://github.com/angular/angular/commit/91df739b8022e2177bcc7214ed9a4e94098a4831) | fix | prevent caching of responses with Set-Cookie headers |
### service-worker
| Commit | Type | Description |
| -- | -- | -- |
| [1804f73bec](https://github.com/angular/angular/commit/1804f73becf425b5b1ccee9751d6e3fbd10e1f0b) | fix | preserve referrer in asset requests |
| [e86c31bf26](https://github.com/angular/angular/commit/e86c31bf26359aacce814167506dbdae585a7eb8) | fix | preserve referrer policy in asset requests |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.17"></a>
# 21.2.17 (2026-06-10)
## Deprecations
### platform-server
- XHR support in `@angular/platform-server` is deprecated. Use standard `fetch` APIs instead.
### common
| Commit | Type | Description |
| -- | -- | -- |
| [86a56dc279](https://github.com/angular/angular/commit/86a56dc279e71159d09a073a3cb138f49131995b) | fix | Limits date format string length |
| [d846326b07](https://github.com/angular/angular/commit/d846326b071e0a4ab090e068d934b182926c6b15) | fix | skip transfer cache for uncacheable HTTP traffic |
| [bc55749698](https://github.com/angular/angular/commit/bc55749698ce3917160cd8e9f7108f3c5d1c0b32) | fix | use cryptographically secure SHA-256 for transfer cache key generation |
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [dc9c99636d](https://github.com/angular/angular/commit/dc9c99636d3471ed5a3c5cda54b95f604cd2b9a4) | fix | sanitize two-way properties |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [1523061137](https://github.com/angular/angular/commit/152306113760e13196653699b42046d9f4129a37) | fix | harden TransferState restoration against DOM clobbering |
| [88832c84f8](https://github.com/angular/angular/commit/88832c84f8a3cd88d80adcde539a6f91a1f30b74) | fix | validate lowercase SVG animation attribute names ([#69269](https://github.com/angular/angular/pull/69269)) |
### http
| Commit | Type | Description |
| -- | -- | -- |
| [bcb1b7ea25](https://github.com/angular/angular/commit/bcb1b7ea2575b140f7bf202ad4f779e402cd6094) | fix | preserve empty referrer option in HttpRequest |
| [a810a319d1](https://github.com/angular/angular/commit/a810a319d10a8254307eb8f0598e7a888ce09ec0) | fix | Rejects non-HTTP(S) URLs in JSONP requests |
| [e245d40c4d](https://github.com/angular/angular/commit/e245d40c4d05665ab4814c594b8e0849b6e88a2d) | fix | skip transfer cache for fetch credentialed requests |
### platform-server
| Commit | Type | Description |
| -- | -- | -- |
| [35510746b7](https://github.com/angular/angular/commit/35510746b7d6b5c3de41de04c0586fc286c9e748) | fix | harden platform location origin validation during SSR |
| [13fb0afe93](https://github.com/angular/angular/commit/13fb0afe93b45e3c2383969f70d3ee1f0146ecfb) | refactor | deprecate ServerXhr ([#69255](https://github.com/angular/angular/pull/69255)) |
### service-worker
| Commit | Type | Description |
| -- | -- | -- |
| [b9d29381bb](https://github.com/angular/angular/commit/b9d29381bb4442164b19d9b7e0baa147a7b25629) | fix | Strips sensitive headers on cross-origin redirects |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.16"></a>
# 21.2.16 (2026-06-03)
### common
| Commit | Type | Description |
| -- | -- | -- |
| [f6d8e642b0](https://github.com/angular/angular/commit/f6d8e642b0b215d2f9dbf1060abd24348c6cbf66) | fix | only strip a literal /index.html suffix from URLs |
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [ae1c8a1f7a](https://github.com/angular/angular/commit/ae1c8a1f7a7f1d4832da3b22e3763864fa5ff098) | fix | move projection attributes into constants |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [3fd6897a67](https://github.com/angular/angular/commit/3fd6897a67fd6acdc01fcde0452a98c3e0f81e21) | fix | harden inherit definition feature against polluted prototypes |
| [7e38336dc7](https://github.com/angular/angular/commit/7e38336dc73e14d98cc6465f54e1b7d6271facb2) | fix | use Object.create(null) for LOCALE_DATA as a hardening measure |
### platform-server
| Commit | Type | Description |
| -- | -- | -- |
| [66821c4ed5](https://github.com/angular/angular/commit/66821c4ed5f580912a1609fc1e06a86f8793c2cf) | fix | throw on suspicious URLs and restrict protocol-relative URLs |
| [d3170031b6](https://github.com/angular/angular/commit/d3170031b6f35508f960cba18586843925bb61ec) | fix | update domino to latest version |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.15"></a>
# 21.2.15 (2026-05-28)
### common
| Commit | Type | Description |
| -- | -- | -- |
| [7f4ac78994](https://github.com/angular/angular/commit/7f4ac78994bff1576ab33f3ce48f95c17f40b4d8) | fix | add upper bounds for digitsInfo |
| [300f61feb3](https://github.com/angular/angular/commit/300f61feb3a534bfddf16fcbd240f97b32249699) | fix | sanitize placeholder |
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [0b07f47bd6](https://github.com/angular/angular/commit/0b07f47bd6598ae6bd5b75a375e2c817a3c0f243) | fix | normalize tag names with custom namespaces in DomElementSchemaRegistry ([#68925](https://github.com/angular/angular/pull/68925)) |
| [eb1cbbf2eb](https://github.com/angular/angular/commit/eb1cbbf2eb5833219a367a61c04eb07aaa36cc29) | fix | prevent namespaced SVG <style> elements from being stripped |
| [cc1378d54b](https://github.com/angular/angular/commit/cc1378d54bd93f3882d732261be8e66720eb71b2) | fix | sanitize dynamic href and xlink:href bindings on SVG a elements ([#68925](https://github.com/angular/angular/pull/68925)) |
| [782e01594e](https://github.com/angular/angular/commit/782e01594e2ad9134c7385dcf3b518101b23ccab) | fix | strip namespaced SVG script elements during template compilation ([#68925](https://github.com/angular/angular/pull/68925)) |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [ff12fe55ac](https://github.com/angular/angular/commit/ff12fe55ace5e861ba261afb4c0480ff3c40a192) | fix | normalize tag names in runtime i18n attribute security context lookup ([#68925](https://github.com/angular/angular/pull/68925)) |
| [e6fe77cc97](https://github.com/angular/angular/commit/e6fe77cc97fd10351687416f938bf754aff4eb9f) | fix | sanitize meta selectors |
| [daaf32937f](https://github.com/angular/angular/commit/daaf32937fd5c46e411b26f7c082613716fe9550) | fix | support prefix-insensitive DOM schema lookups and compile-time i18n attribute validation ([#68925](https://github.com/angular/angular/pull/68925)) |
| [dada86e43d](https://github.com/angular/angular/commit/dada86e43d847204f714d1a933084617ab941c0a) | fix | synchronize core sanitization schema with compiler ([#68925](https://github.com/angular/angular/pull/68925)) |
### http
| Commit | Type | Description |
| -- | -- | -- |
| [582a417bd2](https://github.com/angular/angular/commit/582a417bd27fdaf989e5065dbcdf1ad752faf70c) | fix | exclude withCredentials requests from transfer cache |
| [5c6d6df34b](https://github.com/angular/angular/commit/5c6d6df34bbeff3ce98f3b35875444f925cc8f51) | fix | skip TransferCache for cookie-bearing requests by default |
### platform-server
| Commit | Type | Description |
| -- | -- | -- |
| [37e8aadf87](https://github.com/angular/angular/commit/37e8aadf87b4facfcaf002a1557f8c393a362d97) | fix | prevent SSRF bypasses via backslash URLs in HttpClient |
| [72696e244e](https://github.com/angular/angular/commit/72696e244ed7646cca9ab9afc7769a2163943bda) | fix | secure location and document initialization against SSRF and path hijack |
### service-worker
| Commit | Type | Description |
| -- | -- | -- |
| [b8bd49341d](https://github.com/angular/angular/commit/b8bd49341ddcee10d119a9d4aa8e5736e4e5da53) | fix | Preserves explicit 'credentials: omit' in asset requests |
| [ca32fc1000](https://github.com/angular/angular/commit/ca32fc10001301e6174804f9abcfba62252334f4) | fix | Preserves HTTP cache mode in asset group requests |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.14"></a>
# 21.2.14 (2026-05-20)
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [68282dff9f](https://github.com/angular/angular/commit/68282dff9f9ef46540cca4bd38fc1ab739c8a783) | fix | strip namespaced SVG script elements during template compilation |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [c0f52272ed](https://github.com/angular/angular/commit/c0f52272ed337d4776bd4178cbbdc7f32037500f) | fix | do not insert todo when migrating void @Output |
| [938a7f3edd](https://github.com/angular/angular/commit/938a7f3eddda97a39edb9edcc8b4dd970858b3a2) | fix | makes resource URL sanitizer lookup case-insensitive |
| [0fb2724194](https://github.com/angular/angular/commit/0fb272419407a64a0a47096b03a911f4e7e83d79) | fix | reject script element as a dynamic component host |
| [49113ac0ef](https://github.com/angular/angular/commit/49113ac0eff852d987b5acb28a9bbda0242842cd) | fix | visit ICU expressions in signal migration schematics |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [099bf577ee](https://github.com/angular/angular/commit/099bf577ee8f0bab60593a8fd2a1de7d298e3cd6) | fix | skip scroll-to-top on initial navigation when hydrating |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.13"></a>
# 21.2.13 (2026-05-13)
### core
| Commit | Type | Description |
| -- | -- | -- |
| [1c6553e97d](https://github.com/angular/angular/commit/1c6553e97d9655d8c48fbf625987fae86f9cd947) | fix | disallow event attribute bindings in host bindings unconditionally |
### platform-server
| Commit | Type | Description |
| -- | -- | -- |
| [629905d537](https://github.com/angular/angular/commit/629905d537f59dc3c264c49f6347e3599dea0215) | fix | add `allowedHosts` option to `renderModule` and `renderApplication` |
| [0b7192f441](https://github.com/angular/angular/commit/0b7192f4410d055191ac9b15bff57d1d0b9a644f) | fix | forward BEFORE_APP_SERIALIZED errors to ErrorHandler |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.12"></a>
# 21.2.12 (2026-05-06)
### core
| Commit | Type | Description |
| -- | -- | -- |
| [fe13bb669d](https://github.com/angular/angular/commit/fe13bb669d2bfab4713623d17b41c430aa0a61d8) | fix | allow explicit read generic with signal input transforms |
| [3430251fef](https://github.com/angular/angular/commit/3430251fef93f6aec1fa9c7867e85df23f67c9a0) | fix | i18n flags leaking on errors |
| [1aeebbe304](https://github.com/angular/angular/commit/1aeebbe3048b5aa612dd0a5448de9883ed51e7e8) | fix | respect ngSkipHydration on components with projectable nodes in LContainers |
| [9e38ed7d57](https://github.com/angular/angular/commit/9e38ed7d5773a9193ba07afdba3f7a9f2fe02d18) | fix | sanitizer typings |
| [7a05a9a71a](https://github.com/angular/angular/commit/7a05a9a71a5ab75042ec5560c01526de6e61e062) | fix | validate security-sensitive attributes in i18n bindings |
| [c37f6ca42f](https://github.com/angular/angular/commit/c37f6ca42f263353cb9563fa90d7b31d3c7837ca) | fix | visit ng-let expression value in signal migration schematics |
### forms
| Commit | Type | Description |
| -- | -- | -- |
| [03ad53863b](https://github.com/angular/angular/commit/03ad53863bf3c368f0f02a4322d4141e8f70f674) | fix | prohibit concurrent submits in signal forms |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.11"></a>
# 21.2.11 (2026-04-29)
### common
| Commit | Type | Description |
| -- | -- | -- |
| [10ad3c0692](https://github.com/angular/angular/commit/10ad3c06923453ae0ec06b06e664ce05900a4ff6) | fix | prevent focus from scrollToAnchor |
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [4f5d8a2c0b](https://github.com/angular/angular/commit/4f5d8a2c0b5e38d4debc4293945270cea4a9590d) | fix | let declaration span not including end character |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [a40e2cebc8](https://github.com/angular/angular/commit/a40e2cebc878965c3e21bfb61658f3f80cbd2ebf) | fix | fix ordering of view queries metadata in JIT mode |
| [885a1a1d97](https://github.com/angular/angular/commit/885a1a1d9757adfa8766d9b369c848a277438c31) | fix | guard against non-object events and avoid listener wrapper identity mismatch |
| [7a64aff9b5](https://github.com/angular/angular/commit/7a64aff9b59999077ea915486a7fa0b97a286659) | fix | prevent event replay double-invocation when element hydrates before app stability |
### platform-server
| Commit | Type | Description |
| -- | -- | -- |
| [be1f80a253](https://github.com/angular/angular/commit/be1f80a253b8ee27ed7d8de2287d6895c4821909) | fix | ensure origin has a trailing slash when parsing url |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.10"></a>
# 21.2.10 (2026-04-22)
### docs
| Commit | Type | Description |
| -- | -- | -- |
| [0d5ee9ae1b](https://github.com/angular/angular/commit/0d5ee9ae1ba4b7acd8f27a059a778f0b4bd8a5bd) | fix | link formatting in "Animating your Application with CSS" |
### migrations
| Commit | Type | Description |
| -- | -- | -- |
| [5533ab4f56](https://github.com/angular/angular/commit/5533ab4f56f574bc9365cf0573c4a34a3ab5aaf1) | fix | fix NgClass leaving trailing comma after removal |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [580212c995](https://github.com/angular/angular/commit/580212c995751c4bf4ce8a49df4167498743e0ea) | fix | restore internal URL on popstate when `browserUrl` is used |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.9"></a>
# 21.2.9 (2026-04-15)
### core
| Commit | Type | Description |
| -- | -- | -- |
| [f603d4714f](https://github.com/angular/angular/commit/f603d4714fa184aad34a6f7f9ea4e79c8af3afac) | fix | escape forward slashes in transfer state to prevent crawler indexing |
### http
| Commit | Type | Description |
| -- | -- | -- |
| [540536c386](https://github.com/angular/angular/commit/540536c386f2c735a700c2c9e2697a88dcb3d4ec) | fix | add CSP nonce support to JsonpClientBackend |
| [63a857b874](https://github.com/angular/angular/commit/63a857b874172766451aa75ed3347ba50f0ee229) | fix | Don't on Passthru outside of reactive context |
### platform-server
| Commit | Type | Description |
| -- | -- | -- |
| [e0b5078cf2](https://github.com/angular/angular/commit/e0b5078cf2ebe79a6de85e9123148ae948b3d81d) | fix | prevent SSRF bypasses via protocol-relative and backslash URLs |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [684e9fd53d](https://github.com/angular/angular/commit/684e9fd53daacb9e910f42d98c6017f9e5cb4180) | fix | normalize multiple leading slashes in URL parser |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.8"></a>
# 21.2.8 (2026-04-08)
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [e40d378f3e](https://github.com/angular/angular/commit/e40d378f3e3e7e57a45c8fbd9565ee06a3a6a13f) | fix | handle nested brackets in host object bindings |
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [2c6781071f](https://github.com/angular/angular/commit/2c6781071f52d6378a002fba6611bb283fbb2fde) | fix | error for type parameter declarations |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [82192deda9](https://github.com/angular/angular/commit/82192deda9c07113835e6c85af3f2c8c8218cda0) | fix | handle missing serialized container hydration data |
| [057cc6d09d](https://github.com/angular/angular/commit/057cc6d09d234f401a810cfdd3ad14127652b88b) | fix | remove obsolete iOS cursor pointer hack in event delegation |
### language-service
| Commit | Type | Description |
| -- | -- | -- |
| [7797671257](https://github.com/angular/angular/commit/7797671257350665e8b3ceb2bc6a0201829dd338) | fix | get quick info at local var location to align with TS semantics and support type narrowing |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.7"></a>
# 21.2.7 (2026-04-01)
### compiler
+2 -2
View File
@@ -71,7 +71,7 @@ You can file new issues by selecting from our [new issue templates](https://gith
### <a name="pr-quality"></a> Contribution Quality
We strongly value open source contribution and pull requests from community contributors. Please note that every pull request is reviewed and merged by an actual person on the team, which does take time and effort. That is time and effort that does take away from other valuable work. With that in mind we have an minimum set of expectations that are required of any community contribution pull request that is opened.
We strongly value open source contribution and pull requests from community contributors. Please note that every pull request is reviewed and merged by an actual person on the team, which does take time and effort. That is time and effort that does take away from other valuable work. With that in mind we have a minimum set of expectations that are required of any community contribution pull request that is opened.
1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR that relates to your submission.
- You don't want to duplicate existing efforts.
@@ -238,7 +238,7 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
- For corporations, we'll need you to
[print, sign and one of scan+email, fax or mail the form][corporate-cla].
If you have more than one GitHub accounts, or multiple email addresses associated with a single GitHub account, you must sign the CLA using the primary email address of the GitHub account used to author Git commits and send pull requests.
If you have more than one GitHub account, or multiple email addresses associated with a single GitHub account, you must sign the CLA using the primary email address of the GitHub account used to author Git commits and send pull requests.
The following documents can help you sort out issues with GitHub accounts and multiple email addresses:
+16 -18
View File
@@ -5,41 +5,41 @@ module(
)
bazel_dep(name = "rules_pkg", version = "1.2.0")
bazel_dep(name = "rules_nodejs", version = "6.7.3")
bazel_dep(name = "aspect_rules_ts", version = "3.8.7")
bazel_dep(name = "rules_nodejs", version = "6.7.4")
bazel_dep(name = "aspect_rules_ts", version = "3.8.8")
bazel_dep(name = "aspect_rules_js", version = "3.0.3")
bazel_dep(name = "aspect_rules_esbuild", version = "0.25.1")
bazel_dep(name = "aspect_rules_jasmine", version = "2.0.4")
bazel_dep(name = "aspect_rules_rollup", version = "2.0.1")
bazel_dep(name = "bazel_skylib", version = "1.9.0")
bazel_dep(name = "bazel_lib", version = "3.2.2")
bazel_dep(name = "tar.bzl", version = "0.9.0")
bazel_dep(name = "yq.bzl", version = "0.3.5")
bazel_dep(name = "bazel_lib", version = "3.3.1")
bazel_dep(name = "tar.bzl", version = "0.10.1")
bazel_dep(name = "yq.bzl", version = "0.3.6")
bazel_dep(name = "rules_angular")
git_override(
module_name = "rules_angular",
commit = "32ce54318d9ec5d84269d4acecbc39944cd8b5e7",
commit = "b1d295334e70335dab7ac9984a989fae0a9c9dc2",
remote = "https://github.com/angular/rules_angular.git",
)
bazel_dep(name = "devinfra")
git_override(
module_name = "devinfra",
commit = "616a50d0b747031b7ea052733adf3771fa6cace9",
commit = "48c48fa3848de5bb0ec1c3203558f099765165ab",
remote = "https://github.com/angular/dev-infra.git",
)
bazel_dep(name = "rules_sass")
git_override(
module_name = "rules_sass",
commit = "13918bec49cd183a591e3781d1d08044b4aa9f61",
commit = "a65d937fabd87672f7245aeaa5eb5a0c5a2abead",
remote = "https://github.com/angular/rules_sass.git",
)
bazel_dep(name = "rules_browsers")
git_override(
module_name = "rules_browsers",
commit = "afdc95c1ce8ed9ff0cb94f829b7fc988c43dd783",
commit = "b03f09ef28a08f8ae07482851cf5ecbf6ac23a2a",
remote = "https://github.com/angular/rules_browsers.git",
)
@@ -71,8 +71,8 @@ use_repo(node, "nodejs_windows_amd64")
pnpm = use_extension("@aspect_rules_js//npm:extensions.bzl", "pnpm")
pnpm.pnpm(
name = "pnpm",
pnpm_version = "10.33.0",
pnpm_version_integrity = "sha512-EFaLtKavtYyes2MNqQzJUWQXq+vT+rvmc58K55VyjaFJHp21pUTHatjrdXD1xLs9bGN7LLQb/c20f6gjyGSTGQ==",
pnpm_version = "10.33.2",
pnpm_version_integrity = "sha512-qQ+vb+6rca1sblf5Tg/hoS9dzCLNdU20CulZPraj4LaxLjVAIYuzeuCDQEsfLObbKkEh6XmCm0r/lLmfSdoc+A==",
)
use_repo(pnpm, "pnpm")
@@ -125,14 +125,12 @@ use_repo(rules_ts_ext, **{"npm_typescript": "angular_npm_typescript"})
# TODO: Figure out how to make ng_project update whenever the packages/core::pkg target changes.
rules_angular = use_extension("@rules_angular//setup:extensions.bzl", "rules_angular")
use_repo_rule("@rules_angular//setup:repositories.bzl", "configurable_deps_repo")(
name = "rules_angular_configurable_deps",
angular_compiler_cli = "@angular//:node_modules/@angular/compiler-cli",
typescript = "@angular//:node_modules/typescript",
rules_angular.setup(
name = "angular_rules_angular_configurable_deps",
angular_compiler_cli = "//:node_modules/@angular/compiler-cli",
typescript = "//:node_modules/typescript",
)
override_repo(rules_angular, "rules_angular_configurable_deps")
use_repo(rules_angular, rules_angular_configurable_deps = "angular_rules_angular_configurable_deps")
register_toolchains(
"@devinfra//bazel/git-toolchain:git_linux_toolchain",
+35 -33
View File
@@ -28,8 +28,8 @@
"https://bcr.bazel.build/modules/aspect_rules_js/3.0.3/source.json": "bb8fff9a304452e1042af9522ad1d54d6f1d1fdf71c5127deadb6fd156654193",
"https://bcr.bazel.build/modules/aspect_rules_rollup/2.0.1/MODULE.bazel": "296e3a053658c2af989ba9bd62a205e6d1fa84bdd6dd5249196546e6b84770ec",
"https://bcr.bazel.build/modules/aspect_rules_rollup/2.0.1/source.json": "2fe8ac1ccb4de74bf884761e070010280b272d94e3997205b361b91c75409726",
"https://bcr.bazel.build/modules/aspect_rules_ts/3.8.7/MODULE.bazel": "830f8a53bb9f1139c24006a90ddc0230481326d69fa847eb00daf8eaae118724",
"https://bcr.bazel.build/modules/aspect_rules_ts/3.8.7/source.json": "95549d64e28f3e4e3648cc037cefdac01ec3b0f58fced2409c286784e82ad0f0",
"https://bcr.bazel.build/modules/aspect_rules_ts/3.8.8/MODULE.bazel": "b52b929a948438665809d49af610f58d1b14f63d6d21ab748f47b6050be4c1f6",
"https://bcr.bazel.build/modules/aspect_rules_ts/3.8.8/source.json": "5414530b761a45ab7ca6c49f0a2a9cf8dc0da772f5037cf05ca18aaa64bb1b19",
"https://bcr.bazel.build/modules/aspect_tools_telemetry/0.2.6/MODULE.bazel": "cafb8781ad591bc57cc765dca5fefab08cf9f65af363d162b79d49205c7f8af7",
"https://bcr.bazel.build/modules/aspect_tools_telemetry/0.2.8/MODULE.bazel": "aa975a83e72bcaac62ee61ab12b788ea324a1d05c4aab28aadb202f647881679",
"https://bcr.bazel.build/modules/aspect_tools_telemetry/0.3.3/MODULE.bazel": "37c764292861c2f70314efa9846bb6dbb44fc0308903b3285da6528305450183",
@@ -53,7 +53,8 @@
"https://bcr.bazel.build/modules/bazel_lib/3.0.0-rc.0/MODULE.bazel": "d6e00979a98ac14ada5e31c8794708b41434d461e7e7ca39b59b765e6d233b18",
"https://bcr.bazel.build/modules/bazel_lib/3.0.0/MODULE.bazel": "22b70b80ac89ad3f3772526cd9feee2fa412c2b01933fea7ed13238a448d370d",
"https://bcr.bazel.build/modules/bazel_lib/3.2.2/MODULE.bazel": "e2c890c8a515d6bca9c66d47718aa9e44b458fde64ec7204b8030bf2d349058c",
"https://bcr.bazel.build/modules/bazel_lib/3.2.2/source.json": "9e84e115c20e14652c5c21401ae85ff4daa8702e265b5c0b3bf89353f17aa212",
"https://bcr.bazel.build/modules/bazel_lib/3.3.1/MODULE.bazel": "732a0d516cf6400d9b3136e4356258aef1bf91de8d5240f87f0112f098920c1d",
"https://bcr.bazel.build/modules/bazel_lib/3.3.1/source.json": "8e5175d7b4125a39b8941d01e38039934d058e03804f46a2b8fd7ae6316b1ce2",
"https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8",
"https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e",
"https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686",
@@ -161,7 +162,8 @@
"https://bcr.bazel.build/modules/rules_nodejs/6.2.0/MODULE.bazel": "ec27907f55eb34705adb4e8257952162a2d4c3ed0f0b3b4c3c1aad1fac7be35e",
"https://bcr.bazel.build/modules/rules_nodejs/6.5.0/MODULE.bazel": "546d0cf79f36f9f6e080816045f97234b071c205f4542e3351bd4424282a8810",
"https://bcr.bazel.build/modules/rules_nodejs/6.7.3/MODULE.bazel": "c22a48b2a0dbf05a9dc5f83837bbc24c226c1f6e618de3c3a610044c9f336056",
"https://bcr.bazel.build/modules/rules_nodejs/6.7.3/source.json": "a3f966f4415a8a6545e560ee5449eac95cc633f96429d08e87c87775c72f5e09",
"https://bcr.bazel.build/modules/rules_nodejs/6.7.4/MODULE.bazel": "e6a241a55c82e999145553d2e00a08fc6ebadf62b63d108fb5e984696ffd0bd2",
"https://bcr.bazel.build/modules/rules_nodejs/6.7.4/source.json": "34e7a8a3b4c8d630ac0e0492b3fed9dba41fe008a0edf220b7d88fa38ac53698",
"https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc",
"https://bcr.bazel.build/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff",
"https://bcr.bazel.build/modules/rules_pkg/1.2.0/MODULE.bazel": "c7db3c2b407e673c7a39e3625dc05dc9f12d6682cbd82a3a5924a13b491eda7e",
@@ -193,16 +195,16 @@
"https://bcr.bazel.build/modules/stardoc/0.7.1/MODULE.bazel": "3548faea4ee5dda5580f9af150e79d0f6aea934fc60c1cc50f4efdd9420759e7",
"https://bcr.bazel.build/modules/stardoc/0.7.2/MODULE.bazel": "fc152419aa2ea0f51c29583fab1e8c99ddefd5b3778421845606ee628629e0e5",
"https://bcr.bazel.build/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216",
"https://bcr.bazel.build/modules/tar.bzl/0.10.1/MODULE.bazel": "bf5fda5b5ccef8c3c4a5f4886144377386e0baa382972f257acb42dcf40ea908",
"https://bcr.bazel.build/modules/tar.bzl/0.10.1/source.json": "3f1beb35acf53c270a9de493cdc775a985551d7069cfcf24e136b42f683bbb10",
"https://bcr.bazel.build/modules/tar.bzl/0.2.1/MODULE.bazel": "52d1c00a80a8cc67acbd01649e83d8dd6a9dc426a6c0b754a04fe8c219c76468",
"https://bcr.bazel.build/modules/tar.bzl/0.5.1/MODULE.bazel": "7c2eb3dcfc53b0f3d6f9acdfd911ca803eaf92aadf54f8ca6e4c1f3aee288351",
"https://bcr.bazel.build/modules/tar.bzl/0.6.0/MODULE.bazel": "a3584b4edcfafcabd9b0ef9819808f05b372957bbdff41601429d5fd0aac2e7c",
"https://bcr.bazel.build/modules/tar.bzl/0.9.0/MODULE.bazel": "452a22d7f02b1c9d7a22ab25edf20f46f3e1101f0f67dc4bfbf9a474ddf02445",
"https://bcr.bazel.build/modules/tar.bzl/0.9.0/source.json": "c732760a374831a2cf5b08839e4be75017196b4d796a5aa55235272ee17cd839",
"https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43",
"https://bcr.bazel.build/modules/yq.bzl/0.1.1/MODULE.bazel": "9039681f9bcb8958ee2c87ffc74bdafba9f4369096a2b5634b88abc0eaefa072",
"https://bcr.bazel.build/modules/yq.bzl/0.3.2/MODULE.bazel": "0384efa70e8033d842ea73aa4b7199fa099709e236a7264345c03937166670b6",
"https://bcr.bazel.build/modules/yq.bzl/0.3.5/MODULE.bazel": "130c603e54be717bdf84100210f06598a0d2b4b4e01888fb01b70f50f41767ec",
"https://bcr.bazel.build/modules/yq.bzl/0.3.5/source.json": "1ae7bdc03cb26aaa8bd2bceadf65e90d90f0b2d03008ba9a0564da2e21396c39",
"https://bcr.bazel.build/modules/yq.bzl/0.3.6/MODULE.bazel": "985c2a0cb4ad9994bb0e33cc7fae931c91105eeefe3faa355b8f4c258d0607c0",
"https://bcr.bazel.build/modules/yq.bzl/0.3.6/source.json": "678aaf6e291164f3cd761bb3e872e8a151248f413dbb63c5524a50b82a5bc890",
"https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0",
"https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27",
"https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca",
@@ -213,7 +215,7 @@
"moduleExtensions": {
"@@aspect_rules_esbuild+//esbuild:extensions.bzl%esbuild": {
"general": {
"bzlTransitiveDigest": "GnYkDpVOnWnYv+xwyhEi0qjk3Lvp4Wei30PeSIQF5vM=",
"bzlTransitiveDigest": "3l5lERuIkp41OnB+ZcMQkXS5ttpwE3LfWmePbmwDofk=",
"usagesDigest": "6We6zwGoawD9YXqMI0KPaxEKJTnamXBsuOekhFS2D40=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
@@ -428,8 +430,8 @@
},
"@@aspect_rules_ts+//ts:extensions.bzl%ext": {
"general": {
"bzlTransitiveDigest": "GbfZLeEI0W26fAQ89dUljyLGSAkzkr3CkF+VDQ0IFlI=",
"usagesDigest": "CnGVBnDYq2qAfYkDXgJlDcJckUt2NU51jQQX+igoGt8=",
"bzlTransitiveDigest": "dhTbv9E6UfT1WJmmu3ORRPO6AKFJvgBjBxu+BO+u1RY=",
"usagesDigest": "wfAUu+hWfjbN8K5K5tnyIyhWKZOHe5xnDfeOD7CIZZ8=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
@@ -447,8 +449,8 @@
"rules_angular_npm_typescript": {
"repoRuleId": "@@aspect_rules_ts+//ts/private:npm_repositories.bzl%http_archive_version",
"attributes": {
"version": "5.9.3",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"version": "6.0.3",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"urls": [
"https://registry.npmjs.org/typescript/-/typescript-{}.tgz"
]
@@ -457,8 +459,8 @@
"npm_typescript": {
"repoRuleId": "@@aspect_rules_ts+//ts/private:npm_repositories.bzl%http_archive_version",
"attributes": {
"version": "5.9.3",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"version": "6.0.3",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"urls": [
"https://registry.npmjs.org/typescript/-/typescript-{}.tgz"
]
@@ -467,8 +469,8 @@
"npm_rules_browsers_typescript": {
"repoRuleId": "@@aspect_rules_ts+//ts/private:npm_repositories.bzl%http_archive_version",
"attributes": {
"version": "5.9.3",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"version": "6.0.3",
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
"urls": [
"https://registry.npmjs.org/typescript/-/typescript-{}.tgz"
]
@@ -492,7 +494,7 @@
"@@aspect_tools_telemetry+//:extension.bzl%telemetry": {
"general": {
"bzlTransitiveDigest": "cl5A2O84vDL6Tt+Qga8FCj1DUDGqn+e7ly5rZ+4xvcc=",
"usagesDigest": "mS7+2ykIUJH5qIIq/jathl5rcwdqyvYA8HOpHe8d+/Y=",
"usagesDigest": "XReaXejedAKAFKyp+2mfnFkPWXBJzM/VxiOYB4nyseY=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
@@ -501,7 +503,7 @@
"repoRuleId": "@@aspect_tools_telemetry+//:extension.bzl%tel_repository",
"attributes": {
"deps": {
"aspect_rules_ts": "3.8.7",
"aspect_rules_ts": "3.8.8",
"aspect_rules_js": "3.0.3",
"aspect_rules_esbuild": "0.25.1",
"aspect_rules_jasmine": "2.0.4",
@@ -560,24 +562,24 @@
},
"@@rules_angular+//setup:extensions.bzl%rules_angular": {
"general": {
"bzlTransitiveDigest": "fkaH7HMicL3g7/NDaFzlq39kcLopMyQ3KdbDn+5CRzA=",
"usagesDigest": "Li29KHtfoig0M7RQAoy9uoACa/RcVICtFkFkARsVOAA=",
"bzlTransitiveDigest": "aS7Uud1IzoU7PPLzH3s6IfFS4b2fa0SRWDi2/fS4bQU=",
"usagesDigest": "PaJB/TvnSzJTbqGUeIfiFAEjGkG4FEW7es6f6MFMtq8=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
"generatedRepoSpecs": {
"angular_rules_angular_configurable_deps": {
"repoRuleId": "@@rules_angular+//setup:repositories.bzl%configurable_deps_repo",
"attributes": {
"angular_compiler_cli": "@@//:node_modules/@angular/compiler-cli",
"typescript": "@@//:node_modules/typescript"
}
},
"rules_angular_configurable_deps": {
"repoRuleId": "@@rules_angular+//setup:repositories.bzl%configurable_deps_repo",
"attributes": {
"angular_compiler_cli": "@@rules_angular+//:node_modules/@angular/compiler-cli",
"typescript": "@@rules_angular+//:node_modules/typescript"
}
},
"dev_infra_rules_angular_configurable_deps": {
"repoRuleId": "@@rules_angular+//setup:repositories.bzl%configurable_deps_repo",
"attributes": {
"angular_compiler_cli": "@@rules_angular+//:node_modules/@angular/compiler-cli",
"typescript": "@@rules_angular+//:node_modules/typescript"
"angular_compiler_cli": "@@//:node_modules/@angular/compiler-cli",
"typescript": "@@//:node_modules/typescript"
}
}
},
@@ -945,8 +947,8 @@
},
"@@rules_nodejs+//nodejs:extensions.bzl%node": {
"general": {
"bzlTransitiveDigest": "4pUxCNc22K4I+6+4Nxu52Hur12tFRfa1JMsN5mdDv60=",
"usagesDigest": "o//mYtRPOfTZZh8pDGhbI7WGo7UOnSiSLgWYaJCEB+0=",
"bzlTransitiveDigest": "oZFClfRhTTwsYzpxVPkOpOt/r0+OzEfEV37au0jFZ0s=",
"usagesDigest": "dp2HPl9Y2BFhrnM0JALIsJHz02rKnRBpISBHQX4qV7E=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
@@ -4162,7 +4164,7 @@
"@@yq.bzl+//yq:extensions.bzl%yq": {
"general": {
"bzlTransitiveDigest": "UfFMy8CWK4/dVo/tfaSAIYUiDGNAPes5eRllx9O9Q9Q=",
"usagesDigest": "263D9xYtKhXWWCVTxT66bpT89HuZpdkB1AqEir45vbY=",
"usagesDigest": "5cUmZOEOibp2h65JoFppstHiXcjDFil/AG+HgD3avRk=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
+1 -1
View File
@@ -67,7 +67,7 @@ Install the Angular CLI globally:
npm install -g @angular/cli
```
Create workspace:
Create a workspace:
```
ng new [PROJECT NAME]
+6 -6
View File
@@ -5,21 +5,21 @@
"@algolia/requester-browser-xhr": "5.48.0",
"@algolia/requester-node-http": "5.48.0",
"@angular/animations": "workspace:*",
"@angular/aria": "21.2.4",
"@angular/build": "21.2.6",
"@angular/cdk": "21.2.4",
"@angular/cli": "21.2.6",
"@angular/aria": "21.2.9",
"@angular/build": "21.2.9",
"@angular/cdk": "21.2.9",
"@angular/cli": "21.2.9",
"@angular/common": "workspace:*",
"@angular/compiler": "workspace:*",
"@angular/compiler-cli": "workspace:*",
"@angular/core": "workspace:*",
"@angular/docs": "workspace:*",
"@angular/forms": "workspace:*",
"@angular/material": "21.2.4",
"@angular/material": "21.2.9",
"@angular/platform-browser": "workspace:*",
"@angular/platform-server": "workspace:*",
"@angular/router": "workspace:*",
"@angular/ssr": "21.2.6",
"@angular/ssr": "21.2.9",
"@codemirror/autocomplete": "6.20.0",
"@codemirror/commands": "6.10.1",
"@codemirror/lang-angular": "0.1.4",
@@ -10,6 +10,8 @@ import {get} from 'node:https';
import {posix} from 'node:path';
const GITHUB_API = 'https://api.github.com/repos/';
const SHA_REGEX = /^[0-9a-f]{40}$/i;
const BRANCH_REGEX = /^(?!.*\.\.)[a-zA-Z0-9/_.-]+$/;
export class GithubClient {
#token;
@@ -30,6 +32,12 @@ export class GithubClient {
* @returns Promise<string[]>
*/
async getAffectedFiles(baseSha, headSha) {
if (!SHA_REGEX.test(baseSha)) {
throw new Error(`Invalid base SHA: ${baseSha}`);
}
if (!SHA_REGEX.test(headSha)) {
throw new Error(`Invalid head SHA: ${headSha}`);
}
const {files} = JSON.parse(await this.#httpGet(`${this.#api}/compare/${baseSha}...${headSha}`));
return files.map((f) => f.filename);
}
@@ -41,6 +49,9 @@ export class GithubClient {
* @returns Promise<string>
*/
async getShaForBranch(branch) {
if (!BRANCH_REGEX.test(branch)) {
throw new Error(`Invalid branch name: ${branch}`);
}
const sha = await this.#httpGet(`${this.#api}/commits/${branch}`, {
headers: {Accept: 'application/vnd.github.VERSION.sha'},
});
@@ -49,7 +60,7 @@ export class GithubClient {
throw new Error(`Unable to extract the SHA for '${branch}'.`);
}
return sha;
return sha.trim();
}
#httpGet(url, options = {}) {
@@ -8,7 +8,7 @@
//tslint:disable:no-console
import assert from 'node:assert';
import {execSync} from 'node:child_process';
import {execFileSync} from 'node:child_process';
import {existsSync, constants as fsConstants} from 'node:fs';
import {
copyFile,
@@ -41,6 +41,16 @@ export async function updateAssets({repo, assetsPath, destPath}) {
await readFile(buildInfoPath, 'utf-8'),
);
const shaRegex = /^[0-9a-f]{40}$/i;
const branchRegex = /^(?!.*\.\.)[a-zA-Z0-9/_.-]+$/;
if (!shaRegex.test(storedSha)) {
throw new Error(`Invalid SHA in build info: ${storedSha}`);
}
if (!branchRegex.test(storedBranch)) {
throw new Error(`Invalid branch name in build info: ${storedBranch}`);
}
assert(process.env.ANGULAR_READONLY_GITHUB_TOKEN);
const githubApi = new GithubClient(
repo,
@@ -63,6 +73,10 @@ export async function updateAssets({repo, assetsPath, destPath}) {
downstreamBranch = storedBranch;
}
if (!shaRegex.test(latestSha)) {
throw new Error(`Invalid SHA resolved: ${latestSha}`);
}
console.log(`Comparing ${storedSha}...${latestSha}.`);
const affectedFiles = await githubApi.getAffectedFiles(storedSha, latestSha);
const changedFiles = affectedFiles.filter((file) => file.startsWith(`${assetsPath}/`));
@@ -78,14 +92,18 @@ export async function updateAssets({repo, assetsPath, destPath}) {
try {
const execOptions = {cwd: temporaryDir, stdio: 'inherit'};
execSync('git init', execOptions);
execSync(`git remote add origin https://github.com/${repo}.git`, execOptions);
execFileSync('git', ['init'], execOptions);
execFileSync(
'git',
['remote', 'add', 'origin', `https://github.com/${repo}.git`],
execOptions,
);
// fetch a commit
execSync(`git fetch origin ${latestSha}`, execOptions);
execFileSync('git', ['fetch', 'origin', latestSha], execOptions);
// reset this repository's main branch to the commit of interest
execSync('git reset --hard FETCH_HEAD', execOptions);
execFileSync('git', ['reset', '--hard', 'FETCH_HEAD'], execOptions);
// get sha when files where changed
shaWhenFilesChanged = execSync(`git rev-list -1 ${latestSha} "${assetsPath}/"`, {
shaWhenFilesChanged = execFileSync('git', ['rev-list', '-1', latestSha, `${assetsPath}/`], {
encoding: 'utf8',
cwd: temporaryDir,
stdio: ['ignore', 'pipe', 'ignore'],
@@ -27,7 +27,7 @@
[href]="item.path"
target="_blank"
[matTooltip]="item.label"
[matTooltipDisabled]="itemLabel.length < 27"
[matTooltipDisabled]="itemLabel.length < labelTruncationThreshold"
matTooltipPosition="after"
[attr.aria-label]="item.label"
[matTooltipClass]="'API-tooltip'"
@@ -53,9 +53,9 @@
matrixParams: 'ignored',
fragment: 'ignored',
}"
(click)="emitClickOnLink()"
(click)="emitClickOnLink(item)"
[matTooltip]="item.label"
[matTooltipDisabled]="itemLabel.length < 27"
[matTooltipDisabled]="itemLabel.length < labelTruncationThreshold"
matTooltipPosition="after"
[attr.aria-label]="item.label"
[matTooltipClass]="'API-tooltip'"
@@ -76,7 +76,7 @@
<div
class="docs-secondary-nav-header"
[matTooltip]="item.label"
[matTooltipDisabled]="itemLabel.length < 27"
[matTooltipDisabled]="itemLabel.length < labelTruncationThreshold"
matTooltipPosition="after"
[attr.aria-label]="item.label"
[matTooltipClass]="'API-tooltip'"
@@ -106,7 +106,7 @@
item.children && item.level === expandableLevel() && !item.isExpanded
"
[matTooltip]="item.label"
[matTooltipDisabled]="itemLabel.length < 27"
[matTooltipDisabled]="itemLabel.length < labelTruncationThreshold"
matTooltipPosition="after"
[attr.aria-label]="item.label"
[matTooltipClass]="'API-tooltip'"
@@ -6,7 +6,7 @@
list-style: none;
overflow-y: auto;
overflow-x: hidden;
height: 100vh;
height: 100dvh;
padding: 0;
margin: 0;
padding-block: 1.5rem;
@@ -153,5 +153,6 @@ describe('NavigationList', () => {
class FakeNavigationListState {
isOpened = signal(true);
activeNavigationItem = signal(navigationItems.at(1));
crossCategoryOrigin = signal<NavigationItem | undefined>(undefined);
toggleItem(item: NavigationItem) {}
}
@@ -39,7 +39,10 @@ export class NavigationList {
readonly linkClicked = output<void>();
protected readonly labelTruncationThreshold = 27;
private readonly navigationState = inject(NavigationState);
private readonly crossCategoryOrigin = this.navigationState.crossCategoryOrigin;
readonly activeItem = this.navigationState.activeNavigationItem;
@@ -51,10 +54,19 @@ export class NavigationList {
) {
return;
}
const prevParentItem = this.crossCategoryOrigin();
if (prevParentItem) {
this.crossCategoryOrigin.set(undefined);
this.navigationState.toggleItem(prevParentItem);
return;
}
this.navigationState.toggleItem(item);
}
emitClickOnLink(): void {
emitClickOnLink(item: NavigationItem): void {
if (item.isCrossReferenced) {
this.crossCategoryOrigin.set(item.parent);
}
this.linkClicked.emit();
}
@@ -27,14 +27,16 @@
</i>
</span>
<!-- Page title -->
<span [innerHtml]="result.labelHtml"></span>
@if (result.package) {
<span
[innerHTML]="result.package"
class="docs-search-result__label__package"
></span>
}
<!-- Page title and package badge -->
<span class="docs-search-result__label__text">
<span [innerHtml]="result.labelHtml"></span>
@if (result.package) {
<span
[innerHTML]="result.package"
class="docs-search-result__label__package"
></span>
}
</span>
</p>
@if (result.subLabelHtml) {
@@ -46,6 +46,7 @@ dialog {
.docs-search-result-icon {
display: inline-block;
flex-shrink: 0;
i {
display: flex;
@@ -91,7 +92,20 @@ dialog {
&__label {
font-weight: 600;
flex-wrap: wrap;
&__text {
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: 0.25rem 0.5rem;
flex: 1;
min-width: 0;
overflow-wrap: break-word;
> * {
min-width: 0;
}
}
&__package {
font-size: 0.75rem;
@@ -17,5 +17,6 @@ export interface NavigationItem {
contentPath?: string;
status?: 'new' | 'updated';
category?: string;
isCrossReferenced?: boolean;
preserveOtherCategoryOrder?: boolean; // true by default
}
@@ -21,7 +21,6 @@ import {
ParameterEntry,
PipeEntry,
TypeAliasEntry,
EntryType,
} from '../entities.mjs';
import {CliCommand, CliOption} from '../cli-entities.mjs';
@@ -105,6 +104,7 @@ export type FunctionEntryRenderable = FunctionEntry &
export type FunctionSignatureMetadataRenderable = FunctionSignatureMetadata &
DocEntryRenderable & {
params: ParameterEntryRenderable[];
htmlReturnDescription?: string;
};
/** Documentation entity for a block augmented with transformed content for rendering. */
@@ -59,10 +59,16 @@ export function ClassMember(props: {member: MemberEntryRenderable}) {
const memberName = member.name;
const displayName = member.displayName;
const returnType = getMemberType(member);
const label = displayName ?? memberName;
return (
<div id={memberName} className={REFERENCE_MEMBER_CARD}>
<header className={REFERENCE_MEMBER_CARD_HEADER}>
<h3>{displayName ?? memberName}</h3>
<h3>
<a class="docs-anchor" href={'#' + memberName}>
{label}
</a>
</h3>
{isClassMethodEntry(member) && member.signatures.length > 1 ? (
<span>{member.signatures.length} overloads</span>
) : returnType ? (
@@ -23,6 +23,7 @@ import {RawHtml} from './raw-html';
export function ClassMethodInfo(props: {
entry: FunctionSignatureMetadataRenderable;
hideUsageNotes?: boolean;
hideDescription?: boolean;
}) {
const entry = props.entry;
@@ -30,7 +31,9 @@ export function ClassMethodInfo(props: {
<div
className={`${REFERENCE_MEMBER_CARD_ITEM} ${entry.deprecated ? 'docs-reference-card-item-deprecated' : ''}`}
>
<RawHtml value={entry.htmlDescription} className={'docs-function-definition'} />
{!props.hideDescription && (
<RawHtml value={entry.htmlDescription} className={'docs-function-definition'} />
)}
{/* In case when method is overloaded we need to indicate which overload is deprecated */}
{entry.deprecated ? (
<div>
@@ -45,6 +48,9 @@ export function ClassMethodInfo(props: {
<div className={'docs-return-type'}>
<span className={PARAM_KEYWORD_CLASS_NAME}>@returns</span>
<CodeSymbol code={entry.returnType} />
{entry.htmlReturnDescription && (
<RawHtml value={entry.htmlReturnDescription} className="docs-parameter-description" />
)}
</div>
{entry.htmlUsageNotes && !props.hideUsageNotes ? (
<div className={'docs-usage-notes'}>
@@ -32,31 +32,43 @@ import {SectionUsageNotes} from './section-usage-notes';
export const signatureCard = (
name: string,
signature: FunctionSignatureMetadataRenderable,
opts: {id: string; printSignaturesAsHeader: boolean; hideUsageNotes?: boolean},
opts: {
id: string;
printSignaturesAsHeader: boolean;
hideUsageNotes?: boolean;
hideHeader?: boolean;
hideDescription?: boolean;
},
) => {
return (
<div id={opts.id} class={REFERENCE_MEMBER_CARD}>
<header class={REFERENCE_MEMBER_CARD_HEADER}>
{opts.printSignaturesAsHeader ? (
<HighlightTypeScript
code={printInitializerFunctionSignatureLine(
name,
signature,
// Always omit types in signature headers, to keep them short.
true,
)}
/>
) : (
<>
<h3>{name}</h3>
<div>
<CodeSymbol code={signature.returnType} />
</div>
</>
)}
</header>
{!opts.hideHeader && (
<header class={REFERENCE_MEMBER_CARD_HEADER}>
{opts.printSignaturesAsHeader ? (
<HighlightTypeScript
code={printInitializerFunctionSignatureLine(
name,
signature,
// Always omit types in signature headers, to keep them short.
true,
)}
/>
) : (
<>
<h3>{name}</h3>
<div>
<CodeSymbol code={signature.returnType} />
</div>
</>
)}
</header>
)}
<div class={REFERENCE_MEMBER_CARD_BODY}>
<ClassMethodInfo entry={signature} hideUsageNotes={opts.hideUsageNotes} />
<ClassMethodInfo
entry={signature}
hideUsageNotes={opts.hideUsageNotes}
hideDescription={opts.hideDescription}
/>
</div>
</div>
);
@@ -64,8 +76,8 @@ export const signatureCard = (
/** Component to render a function API reference document. */
export function FunctionReference(entry: FunctionEntryRenderable) {
// Use signatures as header if there are multiple signatures.
const printSignaturesAsHeader = entry.signatures.length > 1;
const hideSignatureCardDescription = !printSignaturesAsHeader;
return (
<div className={API_REFERENCE_CONTAINER}>
@@ -73,14 +85,15 @@ export function FunctionReference(entry: FunctionEntryRenderable) {
<DeprecationWarning entry={entry} />
<SectionApi entry={entry} />
<div className={REFERENCE_MEMBERS}>
{entry.signatures.length > 1 &&
entry.signatures.map((s, i) =>
signatureCard(s.name, getFunctionMetadataRenderable(s, entry.moduleName, entry.repo), {
id: `${s.name}_${i}`,
printSignaturesAsHeader,
hideUsageNotes: true,
}),
)}
{entry.signatures.map((s, i) =>
signatureCard(s.name, getFunctionMetadataRenderable(s, entry.moduleName, entry.repo), {
id: `${s.name}_${i}`,
printSignaturesAsHeader,
hideHeader: !printSignaturesAsHeader,
hideUsageNotes: hideSignatureCardDescription,
hideDescription: hideSignatureCardDescription,
}),
)}
</div>
<SectionDescription entry={entry} />
@@ -8,9 +8,9 @@
import {h} from 'preact';
import {ParameterEntryRenderable} from '../entities/renderables.mjs';
import {RawHtml} from './raw-html';
import {PARAM_GROUP_CLASS_NAME} from '../styling/css-classes.mjs';
import {CodeSymbol} from './code-symbols';
import {RawHtml} from './raw-html';
/** Component to render a function or method parameter reference doc fragment. */
export function Parameter(props: {param: ParameterEntryRenderable}) {
@@ -21,7 +21,9 @@ export function Parameter(props: {param: ParameterEntryRenderable}) {
{/*TODO: isOptional, isRestParam*/}
<span class="docs-param-keyword">@param</span>
<span class="docs-param-name">{param.name}</span>
<CodeSymbol code={param.type} />
<span class="docs-param-type">
<CodeSymbol code={param.type} />
</span>
<RawHtml value={param.htmlDescription} className="docs-parameter-description" />
</div>
);
@@ -43,4 +43,17 @@ describe('CLI docs to html', () => {
expect(cliTocs[0].textContent).toContain('ng component [name] [options]');
expect(cliTocs[1].textContent).toContain('ng c [name] [options]');
});
it('should rewrite absolute angular.dev hrefs in option descriptions to root-relative', async () => {
const renderableJson = await getRenderable(entryJson, '', 'angular/cli');
const localFragment = JSDOM.fragment(renderEntry(renderableJson));
const hrefs = Array.from(localFragment.querySelectorAll('a')).map((a) =>
a.getAttribute('href'),
);
// Absolute angular.dev URLs are rewritten to root-relative.
expect(hrefs).toContain('/cli-test-rewrite');
expect(hrefs).not.toContain('https://angular.dev/cli-test-rewrite');
// Subdomains are intentionally left as external.
expect(hrefs).toContain('https://next.angular.dev/cli-test-preview');
});
});
@@ -2,9 +2,7 @@
"name": "generate",
"command": "ng generate <schematic>",
"shortDescription": "Generates and/or modifies files based on a schematic.",
"aliases": [
"g"
],
"aliases": ["g"],
"deprecated": false,
"options": [
{
@@ -16,9 +14,7 @@
{
"name": "dry-run",
"type": "boolean",
"aliases": [
"d"
],
"aliases": ["d"],
"default": false,
"description": "Run through and reports activity without writing out results."
},
@@ -44,6 +40,12 @@
"type": "string",
"description": "The [collection:schematic] to run.",
"positional": 0
},
{
"name": "test-angular-dev-href-rewrite",
"type": "boolean",
"default": false,
"description": "See [angular.dev test](https://angular.dev/cli-test-rewrite) and [preview test](https://next.angular.dev/cli-test-preview)."
}
],
"subcommands": [
@@ -75,17 +77,13 @@
{
"name": "inline-style",
"type": "boolean",
"aliases": [
"s"
],
"aliases": ["s"],
"description": "Include styles inline in the root component.ts file. Only CSS styles can be included inline. Default is false, meaning that an external styles file is created and referenced in the root component.ts file."
},
{
"name": "inline-template",
"type": "boolean",
"aliases": [
"t"
],
"aliases": ["t"],
"description": "Include template inline in the root component.ts file. Default is false, meaning that an external template file is created and referenced in the root component.ts file. "
},
{
@@ -103,9 +101,7 @@
{
"name": "prefix",
"type": "string",
"aliases": [
"p"
],
"aliases": ["p"],
"default": "app",
"description": "A prefix to apply to generated selectors."
},
@@ -135,9 +131,7 @@
{
"name": "skip-tests",
"type": "boolean",
"aliases": [
"S"
],
"aliases": ["S"],
"default": false,
"description": "Do not create \"spec.ts\" test files for the application."
},
@@ -163,29 +157,17 @@
"name": "style",
"type": "string",
"default": "css",
"enum": [
"css",
"scss",
"sass",
"less"
],
"enum": ["css", "scss", "sass", "less"],
"description": "The file extension or preprocessor to use for style files."
},
{
"name": "view-encapsulation",
"type": "string",
"enum": [
"Emulated",
"None",
"ShadowDom",
"ExperimentalIsolatedShadowDom"
],
"enum": ["Emulated", "None", "ShadowDom", "ExperimentalIsolatedShadowDom"],
"description": "The view encapsulation strategy to use in the new application."
}
],
"aliases": [
"app"
],
"aliases": ["app"],
"deprecated": false
},
{
@@ -216,9 +198,7 @@
"description": "Adds a developer-defined type to the filename, in the format \"name.type.ts\"."
}
],
"aliases": [
"cl"
],
"aliases": ["cl"],
"deprecated": false
},
{
@@ -229,22 +209,15 @@
{
"name": "change-detection",
"type": "string",
"aliases": [
"c"
],
"aliases": ["c"],
"default": "Default",
"enum": [
"Default",
"OnPush"
],
"enum": ["Default", "OnPush"],
"description": "The change detection strategy to use in the new component."
},
{
"name": "display-block",
"type": "boolean",
"aliases": [
"b"
],
"aliases": ["b"],
"default": false,
"description": "Specifies if the style will contain `:host { display: block; }`."
},
@@ -269,27 +242,21 @@
{
"name": "inline-style",
"type": "boolean",
"aliases": [
"s"
],
"aliases": ["s"],
"default": false,
"description": "Include styles inline in the component.ts file. Only CSS styles can be included inline. By default, an external styles file is created and referenced in the component.ts file."
},
{
"name": "inline-template",
"type": "boolean",
"aliases": [
"t"
],
"aliases": ["t"],
"default": false,
"description": "Include template inline in the component.ts file. By default, an external template file is created and referenced in the component.ts file."
},
{
"name": "module",
"type": "string",
"aliases": [
"m"
],
"aliases": ["m"],
"description": "The declaring NgModule."
},
{
@@ -301,9 +268,7 @@
{
"name": "prefix",
"type": "string",
"aliases": [
"p"
],
"aliases": ["p"],
"description": "The prefix to apply to the generated component selector."
},
{
@@ -344,13 +309,7 @@
"name": "style",
"type": "string",
"default": "css",
"enum": [
"css",
"scss",
"sass",
"less",
"none"
],
"enum": ["css", "scss", "sass", "less", "none"],
"description": "The file extension or preprocessor to use for style files, or 'none' to skip generating the style file."
},
{
@@ -362,21 +321,12 @@
{
"name": "view-encapsulation",
"type": "string",
"aliases": [
"v"
],
"enum": [
"Emulated",
"None",
"ShadowDom",
"ExperimentalIsolatedShadowDom"
],
"aliases": ["v"],
"enum": ["Emulated", "None", "ShadowDom", "ExperimentalIsolatedShadowDom"],
"description": "The view encapsulation strategy to use in the new component."
}
],
"aliases": [
"c"
],
"aliases": ["c"],
"deprecated": false
},
{
@@ -392,10 +342,7 @@
{
"name": "type",
"type": "string",
"enum": [
"karma",
"browserslist"
],
"enum": ["karma", "browserslist"],
"description": "Specifies which type of configuration file to create.",
"positional": 0
}
@@ -423,9 +370,7 @@
{
"name": "module",
"type": "string",
"aliases": [
"m"
],
"aliases": ["m"],
"description": "The declaring NgModule."
},
{
@@ -437,9 +382,7 @@
{
"name": "prefix",
"type": "string",
"aliases": [
"p"
],
"aliases": ["p"],
"description": "A prefix to apply to generated selectors."
},
{
@@ -471,9 +414,7 @@
"description": "Whether the generated directive is standalone."
}
],
"aliases": [
"d"
],
"aliases": ["d"],
"deprecated": false
},
{
@@ -498,9 +439,7 @@
"description": "Adds a developer-defined type to the filename, in the format \"name.type.ts\"."
}
],
"aliases": [
"e"
],
"aliases": ["e"],
"deprecated": false
},
{
@@ -537,9 +476,7 @@
{
"name": "implements",
"type": "array",
"aliases": [
"guardType"
],
"aliases": ["guardType"],
"description": "Specifies which type of guard to create."
},
{
@@ -560,9 +497,7 @@
"description": "Do not create \"spec.ts\" test files for the new guard."
}
],
"aliases": [
"g"
],
"aliases": ["g"],
"deprecated": false
},
{
@@ -631,9 +566,7 @@
"positional": 1
}
],
"aliases": [
"i"
],
"aliases": ["i"],
"deprecated": false
},
{
@@ -656,9 +589,7 @@
{
"name": "prefix",
"type": "string",
"aliases": [
"p"
],
"aliases": ["p"],
"default": "lib",
"description": "A prefix to apply to generated selectors."
},
@@ -692,9 +623,7 @@
"description": "Creates a library based upon the standalone API, without NgModules."
}
],
"aliases": [
"lib"
],
"aliases": ["lib"],
"deprecated": false
},
{
@@ -711,9 +640,7 @@
{
"name": "module",
"type": "string",
"aliases": [
"m"
],
"aliases": ["m"],
"description": "The declaring NgModule."
},
{
@@ -742,16 +669,11 @@
"name": "routing-scope",
"type": "string",
"default": "Child",
"enum": [
"Child",
"Root"
],
"enum": ["Child", "Root"],
"description": "The scope for the new routing module."
}
],
"aliases": [
"m"
],
"aliases": ["m"],
"deprecated": false
},
{
@@ -774,9 +696,7 @@
{
"name": "module",
"type": "string",
"aliases": [
"m"
],
"aliases": ["m"],
"description": "The declaring NgModule."
},
{
@@ -809,9 +729,7 @@
"description": "Whether the generated pipe is standalone."
}
],
"aliases": [
"p"
],
"aliases": ["p"],
"deprecated": false
},
{
@@ -849,9 +767,7 @@
"description": "Do not create \"spec.ts\" test files for the new resolver."
}
],
"aliases": [
"r"
],
"aliases": ["r"],
"deprecated": false
},
{
@@ -883,9 +799,7 @@
"description": "Do not create \"spec.ts\" test files for the new service."
}
],
"aliases": [
"s"
],
"aliases": ["s"],
"deprecated": false
},
{
@@ -463,6 +463,7 @@
"isRestParam": false
}
],
"returnDescription": "A reference that can be used to unregister callbacks registered by this call.",
"rawComment": "/**\n * Register callbacks to be invoked the next time the application finishes rendering, during the\n * specified phases. The available phases are:\n * - `earlyRead`\n * Use this phase to **read** from the DOM before a subsequent `write` callback, for example to\n * perform custom layout that the browser doesn't natively support. Prefer the `read` phase if\n * reading can wait until after the write phase. **Never** write to the DOM in this phase.\n * - `write`\n * Use this phase to **write** to the DOM. **Never** read from the DOM in this phase.\n * - `mixedReadWrite`\n * Use this phase to read from and write to the DOM simultaneously. **Never** use this phase if\n * it is possible to divide the work among the other phases instead.\n * - `read`\n * Use this phase to **read** from the DOM. **Never** write to the DOM in this phase.\n *\n * <div class=\"docs-alert docs-alert-critical\">\n *\n * You should prefer using the `read` and `write` phases over the `earlyRead` and `mixedReadWrite`\n * phases when possible, to avoid performance degradation.\n *\n * </div>\n *\n * Note that:\n * - Callbacks run in the following phase order *once, after the next render*:\n * 1. `earlyRead`\n * 2. `write`\n * 3. `mixedReadWrite`\n * 4. `read`\n * - Callbacks in the same phase run in the order they are registered.\n * - Callbacks run on browser platforms only, they will not run on the server.\n *\n * The first phase callback to run as part of this spec will receive no parameters. Each\n * subsequent phase callback in this spec will receive the return value of the previously run\n * phase callback as a parameter. This can be used to coordinate work across multiple phases.\n *\n * Angular is unable to verify or enforce that phases are used correctly, and instead\n * relies on each developer to follow the guidelines documented for each value and\n * carefully choose the appropriate one, refactoring their code if necessary. By doing\n * so, Angular is better able to minimize the performance degradation associated with\n * manual DOM access, ensuring the best experience for the end users of your application\n * or library.\n *\n * <div class=\"docs-alert docs-alert-important\">\n *\n * Components are not guaranteed to be [hydrated](guide/hydration) before the callback runs.\n * You must use caution when directly reading or writing the DOM and layout.\n *\n * </div>\n *\n * @param spec The callback functions to register\n * @param options Options to control the behavior of the callback\n *\n * @usageNotes\n *\n * Use `afterNextRender` to read or write the DOM once,\n * for example to initialize a non-Angular library.\n *\n * ### Example\n * ```angular-ts\n * @Component({\n * selector: 'my-chart-cmp',\n * template: `<div #chart>{{ ... }}</div>`,\n * })\n * export class MyChartCmp {\n * @ViewChild('chart') chartRef: ElementRef;\n * chart: MyChart|null;\n *\n * constructor() {\n * afterNextRender({\n * write: () => {\n * this.chart = new MyChart(this.chartRef.nativeElement);\n * }\n * });\n * }\n * }\n * ```\n *\n * @developerPreview\n */",
"returnType": "AfterRenderRef"
},
@@ -497,6 +498,7 @@
"isRestParam": false
}
],
"returnDescription": "A reference that can be used to unregister the callback registered by this call.",
"rawComment": "/**\n * Register a callback to be invoked the next time the application finishes rendering, during the\n * `mixedReadWrite` phase.\n *\n * <div class=\"docs-alert docs-alert-critical\">\n *\n * You should prefer specifying an explicit phase for the callback instead, or you risk significant\n * performance degradation.\n *\n * </div>\n *\n * Note that the callback will run\n * - in the order it was registered\n * - on browser platforms only\n * - during the `mixedReadWrite` phase\n *\n * <div class=\"docs-alert docs-alert-important\">\n *\n * Components are not guaranteed to be [hydrated](guide/hydration) before the callback runs.\n * You must use caution when directly reading or writing the DOM and layout.\n *\n * </div>\n *\n * @param callback A callback function to register\n * @param options Options to control the behavior of the callback\n *\n * @usageNotes\n *\n * Use `afterNextRender` to read or write the DOM once,\n * for example to initialize a non-Angular library.\n *\n * ### Example\n * ```angular-ts\n * @Component({\n * selector: 'my-chart-cmp',\n * template: `<div #chart>{{ ... }}</div>`,\n * })\n * export class MyChartCmp {\n * @ViewChild('chart') chartRef: ElementRef;\n * chart: MyChart|null;\n *\n * constructor() {\n * afterNextRender({\n * write: () => {\n * this.chart = new MyChart(this.chartRef.nativeElement);\n * }\n * });\n * }\n * }\n * ```\n *\n * @publicApi 20.0\n */",
"returnType": "AfterRenderRef"
}
@@ -13,6 +13,8 @@ import {setSymbols} from '../symbol-context.mjs';
import {resolve} from 'path';
import {initHighlighter} from '../../../shared/shiki.mjs';
import {setHighlighterInstance} from '../shiki/shiki.mjs';
import {renderEntry} from '../rendering.mjs';
import {JSDOM} from 'jsdom';
// Note: The tests will probably break if the schema of the api extraction changes.
// All entries in the fake-entries are extracted from Angular's api.
@@ -60,4 +62,21 @@ describe('renderable', () => {
expect(linkedSignal!.experimental).toBe(undefined);
expect(linkedSignal!.stable).toBe(undefined);
});
it('should render docs-anchor links in class member card headers', () => {
const viewRef = entries.get('ViewRef')!;
expect(viewRef).toBeDefined();
const html = renderEntry(viewRef);
const fragment = JSDOM.fragment(html);
const memberCards = fragment.querySelectorAll('.docs-reference-member-card');
expect(memberCards.length).toBeGreaterThan(0);
for (const card of Array.from(memberCards)) {
const id = card.getAttribute('id')!;
const anchor = card.querySelector('h3 a.docs-anchor') as HTMLAnchorElement;
expect(anchor.getAttribute('href')).toBe(`#${id}`);
}
});
});
@@ -199,6 +199,23 @@ describe('jsdoc transforms', () => {
expect(entryFn).toThrowError(/Forbidden relative link: cli\/build ng build/);
});
it('should throw on a miscased absolute @link to a known API symbol', () => {
setSymbols({RouterModule: 'router'});
const entryFn = () =>
addHtmlAdditionalLinks({
jsdocTags: [
{
name: 'see',
comment: '{@link /api/router/routerModule#forRoot forRoot}',
},
],
moduleName: 'test',
});
expect(entryFn).toThrowError(/Broken @link.*Did you mean \/api\/router\/RouterModule/);
});
});
describe('addHtmlDescription', () => {
@@ -53,11 +53,14 @@ export function getCliCardsRenderable(command: CliCommand): CliCardRenderable[]
return cards;
}
// Rewrite absolute angular.dev hrefs to root-relative so links stay in-site.
const angularDevHrefRegex = /(href=["'])https?:\/\/angular\.dev\//g;
function getRenderableOptions(items: CliOption[]): CliOptionRenderable[] {
return items.map((option) => ({
...option,
deprecated: option.deprecated ? {version: undefined} : undefined,
description: marked.parse(option.description) as string,
description: (marked.parse(option.description) as string).replace(angularDevHrefRegex, '$1/'),
}));
}
@@ -95,6 +95,7 @@ export async function addRenderableCodeToc<T extends DocEntry & HasModuleName>(
{
language: 'typescript',
apiEntries: getSymbolsAsApiEntries(),
removeWhitespace: false,
},
);
@@ -20,7 +20,7 @@ import {
setEntryFlags,
} from './jsdoc-transforms.mjs';
import {addModuleName} from './module-name.mjs';
import {addRenderableFunctionParams} from './params-transforms.mjs';
import {addHtmlReturnDescription, addRenderableFunctionParams} from './params-transforms.mjs';
import {addRepo} from './repo.mjs';
/** Given an unprocessed function entry, get the fully renderable function entry. */
@@ -50,11 +50,13 @@ export function getFunctionMetadataRenderable(
repo: string,
): FunctionSignatureMetadataRenderable {
return addHtmlAdditionalLinks(
addRenderableFunctionParams(
addHtmlUsageNotes(
setEntryFlags(
addHtmlJsDocTagComments(
addHtmlDescription(addRepo(addModuleName(entry, moduleName), repo)),
addHtmlReturnDescription(
addRenderableFunctionParams(
addHtmlUsageNotes(
setEntryFlags(
addHtmlJsDocTagComments(
addHtmlDescription(addRepo(addModuleName(entry, moduleName), repo)),
),
),
),
),
@@ -112,7 +112,7 @@ export function addHtmlUsageNotes<T extends HasJsDocTags>(entry: T): T & HasHtml
}
/** Given a markdown JsDoc text, gets the rendered HTML. */
function getHtmlForJsDocText(text: string): string {
export function getHtmlForJsDocText(text: string): string {
const mdToParse = convertLinks(wrapExampleHtmlElementsWithCode(text));
const parsed = parseMarkdown(mdToParse, {
apiEntries: getSymbolsAsApiEntries(),
@@ -223,6 +223,27 @@ function parseAtLink(link: string): {label: string; url: string} | undefined {
);
}
// Validate absolute `/api/...` links against the known symbol registry. This catches
// miscased symbol names (e.g. `/api/router/routerModule` instead of
// `/api/router/RouterModule`) at build time.
if (rawSymbol.startsWith('/api/')) {
const [pathPart] = rawSymbol.split('#');
const segments = pathPart.split('/').filter((s) => s.length > 0);
const symbolName = segments[segments.length - 1];
// Case-insensitive lookup: find the canonical symbol name in the registry.
const knownSymbols = Object.keys(getSymbolsAsApiEntries());
const canonicalSymbol = knownSymbols.find(
(s) => s.toLowerCase() === symbolName.toLowerCase(),
);
if (canonicalSymbol && canonicalSymbol !== symbolName) {
const expectedUrl = getSymbolUrl(canonicalSymbol);
throw Error(
`Broken @link: ${link}. Did you mean ${expectedUrl}? ` +
`Symbol names in API URLs are case-sensitive.`,
);
}
}
return {
url: rawSymbol,
label: description ?? rawSymbol.split('/').pop()!,
@@ -7,7 +7,7 @@
*/
import {HasModuleName, HasParams, HasRenderableParams} from '../entities/traits.mjs';
import {addHtmlDescription} from './jsdoc-transforms.mjs';
import {addHtmlDescription, getHtmlForJsDocText} from './jsdoc-transforms.mjs';
import {addModuleName} from './module-name.mjs';
export function addRenderableFunctionParams<T extends HasParams & HasModuleName>(
@@ -22,3 +22,13 @@ export function addRenderableFunctionParams<T extends HasParams & HasModuleName>
params,
};
}
/** Converts `returnDescription` to `htmlReturnDescription` for rendering. */
export function addHtmlReturnDescription<
T extends {returnDescription?: string; moduleName: string},
>(entry: T): T & {htmlReturnDescription?: string} {
const htmlReturnDescription = entry.returnDescription
? getHtmlForJsDocText(entry.returnDescription)
: undefined;
return {...entry, htmlReturnDescription};
}
@@ -32,6 +32,13 @@ const LINK_EXEMPT = new Set([
'type',
'Host',
'filter',
'required',
'pattern',
'min',
'max',
'minLength',
'maxLength',
'query',
]);
export function shouldLinkSymbol(symbol: string): boolean {
@@ -18,6 +18,7 @@ interface DocsCardToken extends Tokens.Generic {
href?: string;
imgSrc?: string;
iconImgSrc?: string; // Need image since icons are custom
titleInline?: boolean;
tokens: Token[];
}
@@ -30,6 +31,7 @@ const linkRule = /link="([^"]*)"/;
const hrefRule = /href="([^"]*)"/;
const imgSrcRule = /imgSrc="([^"]*)"/;
const iconImgSrcRule = /iconImgSrc="([^"]*)"/;
const titleInlineRule = /(?:^|\s)titleInline(?=\s|$|=)/;
export const docsCardExtension = {
name: 'docs-card',
@@ -47,6 +49,7 @@ export const docsCardExtension = {
const href = hrefRule.exec(attr);
const imgSrc = imgSrcRule.exec(attr);
const iconImgSrc = iconImgSrcRule.exec(attr);
const titleInline = titleInlineRule.test(attr);
const body = match[2].trim();
@@ -59,6 +62,7 @@ export const docsCardExtension = {
link: link ? link[1] : undefined,
imgSrc: imgSrc ? imgSrc[1] : undefined,
iconImgSrc: iconImgSrc ? iconImgSrc[1] : undefined,
titleInline,
tokens: [],
};
this.lexer.blockTokens(token.body, token.tokens);
@@ -79,12 +83,14 @@ function getStandardCard(renderer: AdevDocsRenderer, token: DocsCardToken) {
// We need to read svg content, instead of renering svg with `img`,
// cause we would like to use CSS variables to support dark and light mode.
const icon = loadWorkspaceRelativeFile(token.iconImgSrc);
const header = token.titleInline
? `<div class="docs-card-header-inline">${icon}<h3>${token.title}</h3></div>`
: `${icon}<h3>${token.title}</h3>`;
return `
<a href="${token.href}" ${anchorTarget(token.href)} class="docs-card">
<div>
${icon}
<h3>${token.title}</h3>
${header}
${renderer.parser.parse(token.tokens)}
</div>
<span>${token.link ? token.link : 'Learn more'}</span>
+2 -1
View File
@@ -42,6 +42,7 @@ export function codeToHtml(
apiEntries?: ApiEntries;
language?: string;
highlight?: Set<number>;
removeWhitespace?: boolean;
},
): string {
const html = highlighter.codeToHtml(code, {
@@ -53,7 +54,7 @@ export function codeToHtml(
cssVariablePrefix: '--shiki-',
defaultColor: false,
transformers: [
removeWhitespaceTransformer(),
...(config.removeWhitespace ? [removeWhitespaceTransformer()] : []),
highlightTransformer(config.highlight),
linkApiEntriesTransformer(config.apiEntries),
],
@@ -21,6 +21,7 @@ export class NavigationState {
private readonly _isMobileNavVisible = signal<boolean>(false);
private readonly _level = linkedSignal(() => this._expandedItems().length);
readonly crossCategoryOrigin = signal<NavigationItem | undefined>(undefined);
readonly primaryActiveRouteItem = signal<string | null>(null);
activeNavigationItem = this._activeNavigationItem.asReadonly();
expandedItems = this._expandedItems.asReadonly();
+8 -8
View File
@@ -24,43 +24,43 @@ $screen-xxl: 1800px;
}
@mixin for-tablet-portrait-up {
@media (min-width: calc($screen-xs + 0.01px)) {
@media (min-width: ($screen-xs + 1px)) {
@content;
}
}
@mixin for-tablet {
@media (min-width: calc($screen-xs + 0.01px)) and (max-width: $screen-md) {
@media (min-width: ($screen-xs + 1px)) and (max-width: $screen-md) {
@content;
}
}
@mixin for-tablet-up {
@media (min-width: calc($screen-sm + 0.01px)) {
@media (min-width: ($screen-sm + 1px)) {
@content;
}
}
@mixin for-tablet-landscape-up {
@media (min-width: calc($screen-md + 0.01px)) {
@media (min-width: ($screen-md + 1px)) {
@content;
}
}
@mixin for-desktop-up {
@media (min-width: calc($screen-lg + 0.01px)) {
@media (min-width: ($screen-lg + 1px)) {
@content;
}
}
@mixin for-large-desktop-up {
@media (min-width: calc($screen-xl + 0.01px)) {
@media (min-width: ($screen-xl + 1px)) {
@content;
}
}
@mixin for-extra-large-desktop-up {
@media (min-width: calc($screen-xxl + 0.01px)) {
@media (min-width: ($screen-xxl + 1px)) {
@content;
}
}
@@ -72,7 +72,7 @@ $screen-xxl: 1800px;
}
@mixin for-large-desktop-down {
@media (max-width: calc($screen-xl )) {
@media (max-width: $screen-xl) {
@content;
}
}
+6 -1
View File
@@ -340,6 +340,11 @@
}
}
.docs-param-type {
display: inline-block;
margin-inline-end: 0.5rem;
}
.docs-parameter-description {
p:first-child {
margin-block-start: 0;
@@ -357,7 +362,7 @@
padding-block: 1rem;
// & does not follow a function definition
&:not(.docs-function-definition + .docs-return-type) {
&:not(.docs-function-definition + .docs-return-type):not(:first-child) {
border-block-start: 1px solid var(--senary-contrast);
}
}
+33 -2
View File
@@ -1,6 +1,8 @@
// Card Grid
@mixin docs-card() {
$compact-breakpoint: 550px;
.docs-card-container-wrapper {
border: 1px solid var(--senary-contrast);
border-radius: 0.25rem;
@@ -15,6 +17,11 @@
h2 {
padding: 2.5rem 1rem 2.5rem 2.5rem;
min-width: 350px;
@container docs-content (max-width: $compact-breakpoint) {
min-width: auto;
padding: 1.5rem;
}
}
.theme-fill-bg {
@@ -57,7 +64,11 @@
svg {
fill-opacity: 1;
@container header (max-width: 550px) {
@container header (max-width: $compact-breakpoint) {
display: none;
}
@container docs-content (max-width: $compact-breakpoint) {
display: none;
}
@@ -71,6 +82,11 @@
margin: 1rem;
padding: 1.5rem;
@container docs-content (max-width: $compact-breakpoint) {
margin: 0.5rem;
padding: 0.75rem;
}
.docs-card {
margin: 0;
@@ -143,6 +159,21 @@
margin-block: 1.5rem;
}
.docs-card-header-inline {
display: flex;
align-items: center;
gap: 0.5rem;
svg {
margin-block-end: 0;
flex-shrink: 0;
}
h3 {
margin-block: 0;
}
}
&.docs-card-with-svg {
padding: 0;
@@ -252,7 +283,7 @@
.docs-nav-card-svg {
width: 350px;
@container header (max-width: 550px) {
@container header (max-width: $compact-breakpoint) {
display: none;
}
}
+3 -18
View File
@@ -80,26 +80,11 @@ $code-font-size: 0.875rem;
&:not(pre *) {
position: relative;
padding: 0 0.3rem;
// Fallback for older browsers
background: #e62600;
background: var(--red-to-orange-horizontal-gradient);
background-clip: text;
-webkit-background-clip: text;
color: transparent;
background: var(--subtle-purple);
color: var(--hot-pink);
max-width: max-content;
border-radius: 0.25rem;
width: 100%;
display: inline-block;
&::before {
content: '';
position: absolute;
inset: 0;
width: 100%;
height: 100%;
background: var(--subtle-purple);
border-radius: 0.25rem;
z-index: -1;
}
a:not(.docs-anchor) > & {
position: relative;
+3
View File
@@ -35,6 +35,9 @@
vertical-align: top;
min-width: 10ch;
}
td:has(code) {
min-width: 8ch;
}
&:not(:last-child) {
border-block-end: 1px solid var(--senary-contrast);
}
+7
View File
@@ -40,6 +40,13 @@
}
}
// Lock the page behind the mobile primary-nav drawer. `clip` preserves the primary nav's sticky context.
@include mq.for-phone-only {
&:has(.adev-nav-primary--open) {
overflow: clip;
}
}
&:has(.adev-home) {
.adev-nav {
width: 0;
@@ -58,7 +58,7 @@
<li>
<a
href="https://github.com/angular/angular/issues"
title="Post issues and suggestions on github."
title="Post issues and suggestions on GitHub."
>
Report Issues
</a>
@@ -99,6 +99,9 @@
<li>
<a href="https://angular.az/" title="Azərbaycanca">Azərbaycanca</a>
</li>
<li>
<a href="https://docs.angular.lat/" title="Español">Español</a>
</li>
<li>
<a href="https://angular-docs.tr/" title="Türkçe">Türkçe</a>
</li>
@@ -392,11 +392,11 @@
<a
[href]="ngLinks.GITHUB"
cdkMenuItem
title="Angular Github"
title="Angular GitHub"
target="_blank"
rel="noopener"
>
<!-- Github Icon -->
<!-- GitHub Icon -->
<svg
width="20"
height="20"
@@ -436,6 +436,28 @@
</svg>
</a>
</li>
<li>
<a
[href]="ngLinks.STACKOVERFLOW"
cdkMenuItem
title="Angular Stack Overflow"
target="_blank"
rel="noopener"
>
<!-- Stack Overflow Icon -->
<svg
width="20"
height="20"
viewBox="0 0 26 31"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="m22.18 16.32.05.02-.02.01za19 19 0 0 0-2.45 4.14l-.03.07q-.94 2.19-1.29 4.6v.03a18 18 0 0 0-.05 4.85H.01v-4.88h15.94l.1-.6L.69 20.48l1.28-4.7 15.55 4.1.22-.46-13.96-7.96 2.47-4.22 14.16 8.07.34-.4L9.15 3.47 12.65 0l11.78 11.64 1.25 1.23q-1.97 1.5-3.5 3.44"
/>
</svg>
</a>
</li>
</ul>
</ng-template>
</div>
@@ -94,6 +94,7 @@
width: 100%;
padding-inline: calc(var(--layout-padding) - 1.25rem);
height: auto;
scrollbar-width: none;
padding-block: 0;
}
@@ -139,7 +140,7 @@
position: absolute;
top: 0;
background-color: var(--page-background);
box-shadow: 10px 4px 3px 0 rgba(0, 0, 0, 0.001);
box-shadow: 2px 0 0 0 var(--page-background);
transform: translateX(-100%);
// primary nav: exit delayed
@@ -169,6 +170,7 @@
@include mq.for-tablet {
flex-direction: row;
padding-block-start: 0;
}
// version dropdown button
@@ -233,7 +235,9 @@
@include mq.for-tablet {
flex-direction: row !important;
align-items: center;
margin-inline-end: 1.25rem;
margin-top: 0.25rem;
gap: 0.75rem;
}
@@ -289,6 +293,8 @@
@include mq.for-phone-only {
display: block;
position: relative;
top: 1rem;
}
}
@@ -35,6 +35,7 @@
@include mq.for-tablet-landscape-down {
position: absolute;
height: 100dvh;
}
@media (prefers-reduced-motion: no-preference) {
@@ -160,7 +160,7 @@
<section class="explore-section" id="learn-more">
<div class="title">
<h2>What to learn more about Angular?</h2>
<h2>Want to learn more about Angular?</h2>
<div class="pattern"></div>
</div>
+12 -3
View File
@@ -28,8 +28,12 @@
left: calc(var(--layout-padding) + var(--primary-nav-width));
z-index: 1;
@include mq.for-tablet-down {
justify-content: flex-start;
@include mq.for-tablet-landscape-down {
justify-content: center;
margin-top: 4rem;
}
@include mq.for-phone-only {
margin-top: 1rem;
}
}
@@ -182,7 +186,7 @@ section {
}
.search-field {
@include mq.for-tablet-down() {
@include mq.for-tablet-landscape-down() {
display: none;
}
}
@@ -269,6 +273,11 @@ section {
width: fit-content;
margin: 0 auto 2rem;
@include mq.for-phone-only {
width: auto;
margin: 0 0.5rem 1rem;
}
.tab-background {
position: absolute;
top: 4px;
@@ -69,6 +69,7 @@
border-radius: 0.25rem;
padding: 0;
transform: translateY(-0.7rem);
background: var(--page-background);
li {
list-style: none;
@@ -33,6 +33,7 @@ describe('TutorialPlayground', () => {
class FakeNodeRuntimeSandbox {
init() {}
reset() {}
}
TestBed.configureTestingModule({
@@ -56,4 +57,22 @@ describe('TutorialPlayground', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
it('should not call reset on the sandbox before it is initialized', async () => {
const fakeSandbox = {reset: jasmine.createSpy('reset')} as any;
component['nodeRuntimeSandbox'] = fakeSandbox;
component['isSandboxReady'].set(false);
spyOn<any>(component, 'loadTemplate').and.resolveTo();
await component.changeTemplate(component.templates[1]);
expect(fakeSandbox.reset).not.toHaveBeenCalled();
});
it('should call reset on the sandbox after it is initialized', async () => {
const fakeSandbox = {reset: jasmine.createSpy('reset')} as any;
component['nodeRuntimeSandbox'] = fakeSandbox;
component['isSandboxReady'].set(true);
spyOn<any>(component, 'loadTemplate').and.resolveTo();
await component.changeTemplate(component.templates[1]);
expect(fakeSandbox.reset).toHaveBeenCalled();
});
});
@@ -14,6 +14,7 @@ import {
DestroyRef,
EnvironmentInjector,
PLATFORM_ID,
signal,
Type,
effect,
inject,
@@ -55,6 +56,7 @@ export default class PlaygroundComponent {
protected nodeRuntimeSandbox?: NodeRuntimeSandbox;
protected embeddedEditorComponent?: Type<unknown>;
protected selectedTemplate: PlaygroundTemplate = this.defaultTemplate;
private readonly isSandboxReady = signal(false);
constructor() {
if (this.isServer) {
@@ -84,6 +86,7 @@ export default class PlaygroundComponent {
.subscribe(() => {
this.changeDetectorRef.markForCheck();
this.nodeRuntimeSandbox?.init();
this.isSandboxReady.set(true);
});
}
@@ -99,7 +102,9 @@ export default class PlaygroundComponent {
});
this.selectedTemplate = template;
await this.loadTemplate(template.path);
await this.nodeRuntimeSandbox?.reset();
if (this.isSandboxReady()) {
await this.nodeRuntimeSandbox?.reset();
}
}
private async loadTemplate(tutorialPath: string) {
@@ -79,12 +79,15 @@
:host {
--item-attr-base-mix: 80%;
--item-attr-text: var(--page-background);
.docs-light-mode & {
--item-attr-base-mix: 20%;
--item-attr-text: var(--primary-contrast);
}
@media screen and (prefers-color-scheme: light) {
--item-attr-base-mix: 20%;
--item-attr-text: var(--primary-contrast);
}
}
@@ -110,7 +113,7 @@
var(--symbolic-yellow) var(--item-attr-base-mix),
var(--octonary-contrast)
);
color: var(--page-background);
color: var(--item-attr-text);
}
&.adev-experimental {
@@ -119,7 +122,7 @@
var(--symbolic-green) var(--item-attr-base-mix),
var(--octonary-contrast)
);
color: var(--page-background);
color: var(--item-attr-text);
}
&.adev-deprecated {
@@ -28,10 +28,11 @@
-webkit-tap-highlight-color: transparent;
.adev-reference-list-type-filter-label {
margin-block: 2.5rem 1rem;
margin-block: 1rem;
}
.adev-reference-list-type-filter {
box-sizing: border-box;
display: grid;
grid-template-columns: repeat(6, 1fr);
margin-block: 0;
@@ -45,6 +46,7 @@
}
@container api-ref-page (max-width: 600px) {
grid-template-columns: repeat(4, 1fr);
max-width: 500px;
}
@container api-ref-page (max-width: 500px) {
grid-template-columns: repeat(3, 1fr);
@@ -88,16 +90,70 @@
.adev-reference-list-query-filter {
display: flex;
gap: 1.5rem;
gap: 1rem;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
justify-content: space-between;
* {
box-sizing: border-box;
}
docs-text-field,
docs-select {
width: 100%;
max-width: 350px;
}
@container api-ref-page (max-width: 600px) {
flex-direction: column;
align-items: stretch;
docs-text-field,
docs-select {
width: 100%;
max-width: 500px;
}
}
}
.adev-reference-list-status {
display: flex;
align-items: center;
margin-top: 1rem;
::ng-deep .mat-mdc-chip-listbox {
@container api-ref-page (max-width: 600px) {
width: 100%;
max-width: 500px;
.mdc-evolution-chip-set__chips {
display: grid !important;
grid-template-columns: repeat(2, 1fr);
gap: 8px;
margin-left: 0;
}
.mat-mdc-chip-option {
min-width: 0;
width: 100%;
margin: 0;
}
}
@container api-ref-page (max-width: 350px) {
.mdc-evolution-chip-set__chips {
grid-template-columns: 1fr;
}
}
}
}
}
.adev-reference-list-empty {
text-align: center;
margin-block-start: 2rem;
flex-basis: 100%;
p {
font-size: 1rem;
@@ -108,28 +164,3 @@
width: 100%;
}
}
.adev-reference-list-empty {
flex-basis: 100%;
p {
font-size: 1rem;
}
}
.docs-api-item-label-full {
white-space: nowrap;
}
.map-chip-option {
min-width: 190px;
}
.adev-reference-list-status {
display: flex;
align-items: center;
margin-top: 12px;
label {
margin-right: 8px;
}
}
+58 -37
View File
@@ -14,6 +14,12 @@ export enum ApplicationComplexity {
export interface Step {
step: string;
/**
* Action text rendered as Markdown. Use Markdown link syntax `[text](url)`
* for any links — raw `<a>` HTML tags bypass the custom link renderer in
* `update.component.ts` and will not pick up `target="_blank"` or the
* external-link icon.
*/
action: string;
possibleIn: number;
necessaryAsOf: number;
@@ -155,7 +161,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Advanced,
step: 'gendir',
action:
'Do not rely on `gendir`, instead look at using `skipTemplateCodeGen`. <a href=https://github.com/angular/angular/issues/19339#issuecomment-332607471" target="_blank">Read More</a>',
'Do not rely on `gendir`, instead look at using `skipTemplateCodeGen`. [Read More](https://github.com/angular/angular/issues/19339#issuecomment-332607471)',
},
{
possibleIn: 220,
@@ -239,7 +245,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Basic,
step: 'node 8',
action:
'Make sure you are using <a href="http://www.hostingadvice.com/how-to/update-node-js-latest-version/" target="_blank">Node 8 or later</a>',
'Make sure you are using [Node 8 or later](http://www.hostingadvice.com/how-to/update-node-js-latest-version/)',
},
{
possibleIn: 600,
@@ -248,7 +254,7 @@ export const RECOMMENDATIONS: Step[] = [
windows: false,
step: 'Update to CLI v6',
action:
'Update your Angular CLI, and migrate the configuration to the <a href="https://github.com/angular/angular-cli/wiki/angular-workspace" target="_blank">new angular.json format</a> by running the following:<br/><br/>`NG_DISABLE_VERSION_CHECK=1 npx @angular/cli@6 update @angular/cli@6`<br/>',
'Update your Angular CLI, and migrate the configuration to the [new angular.json format](https://github.com/angular/angular-cli/wiki/angular-workspace) by running the following:<br/><br/>`NG_DISABLE_VERSION_CHECK=1 npx @angular/cli@6 update @angular/cli@6`<br/>',
},
{
possibleIn: 600,
@@ -257,7 +263,7 @@ export const RECOMMENDATIONS: Step[] = [
windows: true,
step: 'Update to CLI v6',
action:
'Update your Angular CLI, and migrate the configuration to the <a href="https://github.com/angular/angular-cli/wiki/angular-workspace" target="_blank">new angular.json format</a> by running the following:<br/><br/>`cmd /C "set "NG_DISABLE_VERSION_CHECK=1" && npx @angular/cli@6 update @angular/cli@6 @angular/core@6"`<br/>',
'Update your Angular CLI, and migrate the configuration to the [new angular.json format](https://github.com/angular/angular-cli/wiki/angular-workspace) by running the following:<br/><br/>`cmd /C "set "NG_DISABLE_VERSION_CHECK=1" && npx @angular/cli@6 update @angular/cli@6 @angular/core@6"`<br/>',
},
{
possibleIn: 600,
@@ -343,7 +349,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Medium,
step: 'strictPropertyInitializer',
action:
'If you have TypeScript configured to be strict (if you have set `strict` to `true` in your `tsconfig.json` file), update your `tsconfig.json` to disable `strictPropertyInitialization` or move property initialization from `ngOnInit` to your constructor. You can learn more about this flag on the <a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#strict-class-initialization">TypeScript 2.7 release notes</a>.',
'If you have TypeScript configured to be strict (if you have set `strict` to `true` in your `tsconfig.json` file), update your `tsconfig.json` to disable `strictPropertyInitialization` or move property initialization from `ngOnInit` to your constructor. You can learn more about this flag on the [TypeScript 2.7 release notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#strict-class-initialization).',
},
{
possibleIn: 600,
@@ -351,7 +357,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Basic,
step: 'update to RxJS 6',
action:
'Remove deprecated RxJS 5 features using <a href="https://github.com/ReactiveX/rxjs-tslint" target="_blank">rxjs-tslint auto update rules</a><br/><br/>For most applications this will mean running the following two commands:<br/><br/>`npx rxjs-tslint`<br/>`rxjs-5-to-6-migrate -p src/tsconfig.app.json`',
'Remove deprecated RxJS 5 features using [rxjs-tslint auto update rules](https://github.com/ReactiveX/rxjs-tslint)<br/><br/>For most applications this will mean running the following two commands:<br/><br/>`npx rxjs-tslint`<br/>`rxjs-5-to-6-migrate -p src/tsconfig.app.json`',
},
{
possibleIn: 600,
@@ -374,7 +380,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Basic,
step: 'TypeScript 3.1',
action:
'Angular now uses TypeScript 3.1, read more about any potential breaking changes: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-1.html',
'Angular now uses TypeScript 3.1, read more about [any potential breaking changes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-1.html).',
},
{
possibleIn: 700,
@@ -382,7 +388,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Basic,
step: 'Node 10',
action:
'Angular has now added support for Node 10: https://nodejs.org/en/blog/release/v10.0.0/',
'Angular has now added support for [Node 10](https://nodejs.org/en/blog/release/v10.0.0/).',
},
{
possibleIn: 700,
@@ -464,7 +470,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Basic,
step: 'use ::ng-deep instead of /deep/',
action:
'Replace `/deep/` with `::ng-deep` in your styles, [read more about angular component styles and ::ng-deep](https://angular.io/guide/component-styles#deprecated-deep--and-ng-deep). `/deep/` and `::ng-deep` both are deprecated but using `::ng-deep` is preferred until the shadow-piercing descendant combinator is [removed from browsers and tools](https://www.chromestatus.com/features/6750456638341120) completely.',
'Replace `/deep/` with `::ng-deep` in your styles, [read more about angular component styles and ::ng-deep](https://angular.io/guide/component-styles#deprecated-deep--and-ng-deep). `/deep/` and `::ng-deep` both are deprecated but using `::ng-deep` is preferred until the shadow-piercing descendant combinator is [removed from browsers and tools](https://chromestatus.com/feature/5045542597951488) completely.',
},
{
possibleIn: 800,
@@ -480,7 +486,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Basic,
step: 'node 10',
action:
'Make sure you are using <a href="http://www.hostingadvice.com/how-to/update-node-js-latest-version/" target="_blank">Node 10 or later</a>.',
'Make sure you are using [Node 10 or later](http://www.hostingadvice.com/how-to/update-node-js-latest-version/).',
},
{
possibleIn: 800,
@@ -573,7 +579,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Basic,
step: 'node 10.13',
action:
'Make sure you are using <a href="http://www.hostingadvice.com/how-to/update-node-js-latest-version/" target="_blank">Node 10.13 or later</a>.',
'Make sure you are using [Node 10.13 or later](http://www.hostingadvice.com/how-to/update-node-js-latest-version/).',
},
{
possibleIn: 900,
@@ -781,9 +787,8 @@ export const RECOMMENDATIONS: Step[] = [
possibleIn: 1000,
necessaryAsOf: 1000,
level: ApplicationComplexity.Basic,
step: 'v10 NodeJS 12',
action:
'Make sure you are using <a href="https://nodejs.org/dist/latest-v12.x/" target="_blank">Node 12 or later</a>.',
step: 'v10 Node.js 12',
action: 'Make sure you are using [Node 12 or later](https://nodejs.org/dist/latest-v12.x/).',
},
{
possibleIn: 1000,
@@ -847,7 +852,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Advanced,
step: 'closure-jsdoc-comments',
action:
"Angular's NPM packages no longer contain jsdoc comments, which are necessary for use with closure compiler (extremely uncommon). This support was experimental and only worked in some use cases. There will be an alternative recommended path announced shortly.",
"Angular's npm packages no longer contain jsdoc comments, which are necessary for use with closure compiler (extremely uncommon). This support was experimental and only worked in some use cases. There will be an alternative recommended path announced shortly.",
},
{
possibleIn: 1000,
@@ -871,7 +876,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Medium,
step: 'esm5-bundles',
action:
"The [Angular Package Format](https://g.co/ng/apf) has been updated to remove `esm5` and `fesm5` formats. These are no longer distributed in our npm packages. If you don't use the CLI, you may need to downlevel Angular code to ES5 yourself.",
"The [Angular Package Format](https://angular.dev/tools/libraries/angular-package-format) has been updated to remove `esm5` and `fesm5` formats. These are no longer distributed in our npm packages. If you don't use the CLI, you may need to downlevel Angular code to ES5 yourself.",
},
{
possibleIn: 1000,
@@ -1335,7 +1340,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Basic,
step: 'TypeScript 4.4',
action:
'Angular now uses TypeScript 4.4, read more about any potential breaking changes: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-4.html',
'Angular now uses TypeScript 4.4, read more about [any potential breaking changes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-4.html).',
},
{
possibleIn: 1300,
@@ -1343,7 +1348,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Basic,
step: 'v13 node',
action:
'Make sure you are using <a href="http://www.hostingadvice.com/how-to/update-node-js-latest-version/" target="_blank">Node 12.20.0 or later</a>',
'Make sure you are using [Node 12.20.0 or later](http://www.hostingadvice.com/how-to/update-node-js-latest-version/)',
},
{
possibleIn: 1300,
@@ -1457,7 +1462,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Basic,
step: 'TypeScript 4.6',
action:
'Angular now uses TypeScript 4.6, read more about any potential breaking changes: https://devblogs.microsoft.com/typescript/announcing-typescript-4-6/',
'Angular now uses TypeScript 4.6, read more about [any potential breaking changes](https://devblogs.microsoft.com/typescript/announcing-typescript-4-6/).',
},
{
@@ -1466,7 +1471,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Basic,
step: 'v14 node',
action:
'Make sure you are using <a href="http://www.hostingadvice.com/how-to/update-node-js-latest-version/" target="_blank">Node 14.15.0 or later</a>',
'Make sure you are using [Node 14.15.0 or later](http://www.hostingadvice.com/how-to/update-node-js-latest-version/)',
},
{
possibleIn: 1400,
@@ -1669,7 +1674,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Basic,
step: 'v15 node support',
action:
'Make sure that you are using a supported version of node.js before you upgrade your application. Angular v15 supports node.js versions: 14.20.x, 16.13.x and 18.10.x. <a href="https://v15.angular.io/guide/update-to-version-15#v15-bc-01" alt="Link to more information about this change">Read further</a>',
'Make sure that you are using a supported version of node.js before you upgrade your application. Angular v15 supports node.js versions: 14.20.x, 16.13.x and 18.10.x. [Read further](https://v15.angular.io/guide/update-to-version-15#v15-bc-01)',
},
{
possibleIn: 1500,
@@ -1677,7 +1682,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Basic,
step: 'v15 ts support',
action:
'Make sure that you are using a supported version of TypeScript before you upgrade your application. Angular v15 supports TypeScript version 4.8 or later. <a href="https://v15.angular.io/guide/update-to-version-15#v15-bc-02" alt="Link to more information about this change">Read further</a>',
'Make sure that you are using a supported version of TypeScript before you upgrade your application. Angular v15 supports TypeScript version 4.8 or later. [Read further](https://v15.angular.io/guide/update-to-version-15#v15-bc-02)',
},
{
possibleIn: 1500,
@@ -1701,7 +1706,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Medium,
step: 'v15 keyframe',
action:
'In v15, the Angular compiler prefixes `@keyframes` in CSS with the component\'s scope. This means that any TypeScript code that relies on `keyframes` names no longer works in v15. Update any such instances to: define keyframes programmatically, use global stylesheets, or change the component\'s view encapsulation. <a href="https://v15.angular.io/guide/update-to-version-15#v15-bc-03" alt="Link to more information about this change">Read further</a>',
"In v15, the Angular compiler prefixes `@keyframes` in CSS with the component's scope. This means that any TypeScript code that relies on `keyframes` names no longer works in v15. Update any such instances to: define keyframes programmatically, use global stylesheets, or change the component's view encapsulation. [Read further](https://v15.angular.io/guide/update-to-version-15#v15-bc-03)",
},
{
possibleIn: 1500,
@@ -1717,7 +1722,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Medium,
step: 'v15 base-decorators',
action:
'Make sure to use decorators in base classes with child classes that inherit constructors and use dependency injection. Such base classes should be decorated with either `@Injectable` or `@Directive` or the compiler returns an error. <a href="https://v15.angular.io/guide/update-to-version-15#v15-bc-05" alt="Link to more information about this change">Read further</a>',
'Make sure to use decorators in base classes with child classes that inherit constructors and use dependency injection. Such base classes should be decorated with either `@Injectable` or `@Directive` or the compiler returns an error. [Read further](https://v15.angular.io/guide/update-to-version-15#v15-bc-05)',
},
{
possibleIn: 1500,
@@ -1725,7 +1730,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Medium,
step: 'v15 setDisabledState',
action:
'In v15, `setDisabledState` is always called when a `ControlValueAccessor` is attached. To opt-out of this behavior, use `FormsModule.withConfig` or `ReactiveFormsModule.withConfig`. <a href="https://v15.angular.io/guide/update-to-version-15#v15-bc-06" alt="Link to more information about this change">Read further</a>',
'In v15, `setDisabledState` is always called when a `ControlValueAccessor` is attached. To opt-out of this behavior, use `FormsModule.withConfig` or `ReactiveFormsModule.withConfig`. [Read further](https://v15.angular.io/guide/update-to-version-15#v15-bc-06)',
},
{
possibleIn: 1500,
@@ -1733,7 +1738,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Advanced,
step: 'v15 canParse',
action:
'Applications that use `canParse` should use `analyze` from `@angular/localize/tools` instead. In v15, the `canParse` method was removed from all translation parsers in `@angular/localize/tools`. <a href="https://v15.angular.io/guide/update-to-version-15#v15-bc-07" alt="Link to more information about this change">Read further</a>',
'Applications that use `canParse` should use `analyze` from `@angular/localize/tools` instead. In v15, the `canParse` method was removed from all translation parsers in `@angular/localize/tools`. [Read further](https://v15.angular.io/guide/update-to-version-15#v15-bc-07)',
},
{
possibleIn: 1500,
@@ -1741,7 +1746,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Basic,
step: 'v15 ActivatedRoutSnapshot',
action:
'Make sure that all `ActivatedRouteSnapshot` objects have a `title` property. In v15, the `title` property is a required property of `ActivatedRouteSnapshot`. <a href="https://v15.angular.io/guide/update-to-version-15#v15-bc-08" alt="Link to more information about this change">Read further</a>',
'Make sure that all `ActivatedRouteSnapshot` objects have a `title` property. In v15, the `title` property is a required property of `ActivatedRouteSnapshot`. [Read further](https://v15.angular.io/guide/update-to-version-15#v15-bc-08)',
},
{
possibleIn: 1500,
@@ -1749,7 +1754,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Advanced,
step: 'v15 RouterOutlet',
action:
'If your tests with `RouterOutlet` break, make sure they don\'t depend on the instantiation order of the corresponding component relative to change detection. In v15, `RouterOutlet` instantiates the component after change detection. <a href="https://v15.angular.io/guide/update-to-version-15#v15-bc-09" alt="Link to more information about this change">Read further</a>',
"If your tests with `RouterOutlet` break, make sure they don't depend on the instantiation order of the corresponding component relative to change detection. In v15, `RouterOutlet` instantiates the component after change detection. [Read further](https://v15.angular.io/guide/update-to-version-15#v15-bc-09)",
},
{
possibleIn: 1500,
@@ -1757,7 +1762,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Basic,
step: 'v15 relativeLinkResolution',
action:
'In v15, `relativeLinkResolution` is not configurable in the Router. It was used to opt out of an earlier bug fix that is now standard. <a href="https://v15.angular.io/guide/update-to-version-15#v15-bc-10" alt="Link to more information about this change">Read further</a>',
'In v15, `relativeLinkResolution` is not configurable in the Router. It was used to opt out of an earlier bug fix that is now standard. [Read further](https://v15.angular.io/guide/update-to-version-15#v15-bc-10)',
},
{
possibleIn: 1500,
@@ -1765,7 +1770,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Medium,
step: 'v15 DATE_PIPE_DEFAULT_OPTIONS',
action:
'Change instances of the `DATE_PIPE_DEFAULT_TIMEZONE` token to use `DATE_PIPE_DEFAULT_OPTIONS` to configure time zones. In v15, the `DATE_PIPE_DEFAULT_TIMEZONE` token is deprecated. <a href="https://v15.angular.io/guide/update-to-version-15#v15-dp-01" alt="Link to more information about this change">Read further</a>',
'Change instances of the `DATE_PIPE_DEFAULT_TIMEZONE` token to use `DATE_PIPE_DEFAULT_OPTIONS` to configure time zones. In v15, the `DATE_PIPE_DEFAULT_TIMEZONE` token is deprecated. [Read further](https://v15.angular.io/guide/update-to-version-15#v15-dp-01)',
},
{
possibleIn: 1500,
@@ -1781,7 +1786,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Medium,
step: 'v15 Injector.get',
action:
'Update instances of `Injector.get()` that use an `InjectFlags` parameter to use an `InjectOptions` parameter. The `InjectFlags` parameter of `Injector.get()` is deprecated in v15. <a href="https://v15.angular.io/guide/update-to-version-15#v15-dp-02" alt="Link to more information about this change">Read further</a>',
'Update instances of `Injector.get()` that use an `InjectFlags` parameter to use an `InjectOptions` parameter. The `InjectFlags` parameter of `Injector.get()` is deprecated in v15. [Read further](https://v15.angular.io/guide/update-to-version-15#v15-dp-02)',
},
{
possibleIn: 1500,
@@ -1789,7 +1794,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Basic,
step: 'v15 TestBed.inject',
action:
'Update instances of `TestBed.inject()` that use an `InjectFlags` parameter to use an `InjectOptions` parameter. The `InjectFlags` parameter of `TestBed.inject()` is deprecated in v15. <a href="https://v15.angular.io/guide/update-to-version-15#v15-dp-01" alt="Link to more information about this change">Read further</a>',
'Update instances of `TestBed.inject()` that use an `InjectFlags` parameter to use an `InjectOptions` parameter. The `InjectFlags` parameter of `TestBed.inject()` is deprecated in v15. [Read further](https://v15.angular.io/guide/update-to-version-15#v15-dp-01)',
},
{
possibleIn: 1500,
@@ -1797,7 +1802,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Medium,
step: 'v15 ngModule in providedIn',
action:
'Using `providedIn: ngModule` for an `@Injectable` and `InjectionToken` is deprecated in v15. <a href="https://v15.angular.io/guide/update-to-version-15#v15-dp-04" alt="Link to more information about this change">Read further</a>',
'Using `providedIn: ngModule` for an `@Injectable` and `InjectionToken` is deprecated in v15. [Read further](https://v15.angular.io/guide/update-to-version-15#v15-dp-04)',
},
{
possibleIn: 1500,
@@ -1805,7 +1810,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Basic,
step: 'v15 providedIn any',
action:
'Using `providedIn: \'any\'` for an `@Injectable` or `InjectionToken` is deprecated in v15. <a href="https://v15.angular.io/guide/update-to-version-15#v15-dp-05" alt="Link to more information about this change">Read further</a>',
"Using `providedIn: 'any'` for an `@Injectable` or `InjectionToken` is deprecated in v15. [Read further](https://v15.angular.io/guide/update-to-version-15#v15-dp-05)",
},
{
possibleIn: 1500,
@@ -1813,7 +1818,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Medium,
step: 'v15 RouterLinkWithHref',
action:
'Update instances of the `RouterLinkWithHref`directive to use the `RouterLink` directive. The `RouterLinkWithHref` directive is deprecated in v15. <a href="https://v15.angular.io/guide/update-to-version-15#v15-dp-06" alt="Link to more information about this change">Read further</a>',
'Update instances of the `RouterLinkWithHref`directive to use the `RouterLink` directive. The `RouterLinkWithHref` directive is deprecated in v15. [Read further](https://v15.angular.io/guide/update-to-version-15#v15-dp-06)',
},
{
possibleIn: 1500,
@@ -1822,7 +1827,7 @@ export const RECOMMENDATIONS: Step[] = [
material: true,
step: 'v15 mat refactor',
action:
'In Angular Material v15, many of the components have been refactored to be based on the official Material Design Components for Web (MDC). This change affected the DOM and CSS classes of many components. <a href="https://rc.material.angular.dev/guide/mdc-migration" alt="Link to more information about this change">Read further</a>',
'In Angular Material v15, many of the components have been refactored to be based on the official Material Design Components for Web (MDC). This change affected the DOM and CSS classes of many components. [Read further](https://rc.material.angular.dev/guide/mdc-migration)',
},
{
possibleIn: 1500,
@@ -2669,7 +2674,7 @@ export const RECOMMENDATIONS: Step[] = [
level: ApplicationComplexity.Basic,
step: '20.0.0_update_nodejs_version',
action:
'Ensure your Node.js version is at least 20.11.1 and not v18 or v22.0-v22.10 before upgrading to Angular v20. Check https://angular.dev/reference/versions for the full list of supported Node.js versions.',
'Ensure your Node.js version is at least 20.11.1 and not v18 or v22.0-v22.10 before upgrading to Angular v20. Check the [full list of supported Node.js versions](https://angular.dev/reference/versions).',
},
{
possibleIn: 2000,
@@ -2703,6 +2708,14 @@ export const RECOMMENDATIONS: Step[] = [
action:
"Upgrade your project's TypeScript version to at least 5.8 before upgrading to Angular v20 to ensure compatibility.",
},
{
possibleIn: 2000,
necessaryAsOf: 2000,
level: ApplicationComplexity.Medium,
step: '20.0.0_set_moduleResolution_to_bundler',
action:
"Set `moduleResolution` to `'bundler'` in your `tsconfig.json`. Angular CLI's `ng update` migration applies this change automatically; if you upgrade manually or override the option in a base tsconfig, set it explicitly so imports of secondary entry-points such as `@angular/core/rxjs-interop` continue to resolve correctly.",
},
{
possibleIn: 2000,
necessaryAsOf: 2000,
@@ -2912,4 +2925,12 @@ export const RECOMMENDATIONS: Step[] = [
action:
'The `lastSuccessfulNavigation` property on the Router has been converted to a signal. To get its value, you now need to invoke it as a function: `router.lastSuccessfulNavigation()`.',
},
{
possibleIn: 2100,
necessaryAsOf: 2100,
level: ApplicationComplexity.Medium,
step: '21.0.0-configure-commonengine-allowed-hosts',
action:
"Starting `@angular/ssr` 21.1.5, if your application uses SSR with `CommonEngine`, set the `allowedHosts` option in your `server.ts` (for example, `new CommonEngine({allowedHosts: ['localhost', '*.yourdomain.com']})`). Without it, SSR silently falls back to client-side rendering. This requirement comes from security advisory [GHSA-x288-3778-4hhx](https://github.com/angular/angular-cli/security/advisories/GHSA-x288-3778-4hhx) (also backported to 20.3.17 and 19.2.21).",
},
];
@@ -1,12 +1,13 @@
@use '@angular/docs/styles/media-queries' as mq;
@use '@angular/docs/styles/links' as links;
$ver-dropdown-width: 200px;
$ver-dropdown-width: clamp(165px, 20vw, 200px);
:host {
display: flex;
flex-flow: column;
align-items: center;
padding: var(--layout-padding) 0px;
padding: var(--layout-padding);
container: update-guide-page / inline-size;
.docs-viewer {
@@ -172,7 +173,14 @@ h4 {
}
// Code blocks are generable from the markdown, we need to opt-out of the scoping
::ng-deep code {
cursor: pointer;
::ng-deep {
code {
cursor: pointer;
}
// External links (rendered with target="_blank") get an "open in new" icon
a[target='_blank'] {
@include links.external-link-with-icon;
}
}
}
@@ -13,7 +13,7 @@ import {provideHttpClientTesting} from '@angular/common/http/testing';
import {By} from '@angular/platform-browser';
import UpdateComponent from './update.component';
import {ApplicationComplexity} from './recommendations';
import {ApplicationComplexity, RECOMMENDATIONS} from './recommendations';
describe('UpdateComponent', () => {
let component: UpdateComponent;
@@ -91,4 +91,14 @@ describe('UpdateComponent', () => {
}
});
});
describe('RECOMMENDATIONS data', () => {
it('should use Markdown links instead of raw <a> HTML tags', () => {
for (const step of RECOMMENDATIONS) {
expect(step.action)
.withContext(`step "${step.step}" should use [text](url), not raw <a> HTML`)
.not.toMatch(/<a\s/i);
}
});
});
});
@@ -17,6 +17,20 @@ import {ActivatedRoute, Router} from '@angular/router';
import {marked} from 'marked';
import {MatSnackBar} from '@angular/material/snack-bar';
/**
* Configure marked with a custom link renderer so external links in the
* update guide open in a new tab, matching the convention applied elsewhere
* in adev via the `ExternalLink` directive.
*/
marked.use({
renderer: {
link({href, title, text}) {
const titleAttr = title ? ` title="${title}"` : '';
return `<a href="${href}"${titleAttr} target="_blank" rel="noopener noreferrer">${text}</a>`;
},
},
});
interface Option {
id: keyof Step;
name: string;
@@ -108,7 +122,7 @@ export default class UpdateComponent {
];
protected from = this.versions.find((version) => version.name === '20.0')!;
protected to = this.versions.find((version) => version.name === '21.0')!;
protected futureVersion = 2100;
protected futureVersion = 2200;
protected readonly steps: Step[] = RECOMMENDATIONS;
@@ -496,6 +496,34 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
category: 'Signal Forms',
status: 'new',
},
{
label: 'Cross-field logic',
path: 'guide/forms/signals/cross-field-logic',
contentPath: 'guide/forms/signals/cross-field-logic',
category: 'Signal Forms',
status: 'new',
},
{
label: 'Form submission',
path: 'guide/forms/signals/form-submission',
contentPath: 'guide/forms/signals/form-submission',
category: 'Signal Forms',
status: 'new',
},
{
label: 'Schemas',
path: 'guide/forms/signals/schemas',
contentPath: 'guide/forms/signals/schemas',
category: 'Signal Forms',
status: 'new',
},
{
label: 'Field metadata',
path: 'guide/forms/signals/field-metadata',
contentPath: 'guide/forms/signals/field-metadata',
category: 'Signal Forms',
status: 'new',
},
{
label: 'Async operations',
path: 'guide/forms/signals/async-operations',
@@ -660,6 +688,7 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
path: 'guide/routing/testing',
contentPath: 'guide/routing/testing',
status: 'new',
isCrossReferenced: true,
},
{
label: 'Debugging tests',
@@ -863,6 +892,7 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
label: 'Route transition animations',
path: 'guide/routing/route-transition-animations',
contentPath: 'guide/routing/route-transition-animations',
isCrossReferenced: true,
},
],
},
@@ -1042,16 +1072,15 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
},
// TODO: create those guides
// The signal debugging docs should also be added to the signal section
// {
// label: 'Signals',
// path: 'tools/devtools/signals',
// contentPath: 'tools/devtools/signals',
// },
// {
// label: 'Router',
// path: 'tools/devtools/router',
// contentPath: 'tools/devtools/router',
// }
{
label: 'Router Tree',
path: 'tools/devtools/router',
contentPath: 'tools/devtools/router',
},
],
},
{
+3
View File
@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="25" viewBox="0 -960 960 960" fill="#F637E3">
<path d="M450.001-611.691v-32.386q-39.385-9.923-64.692-41.897-25.308-31.975-25.308-74.025 0-49.922 35.038-84.96 35.039-35.038 84.961-35.038t84.961 35.038q35.038 35.038 35.038 84.96 0 42.05-25.308 74.025-25.307 31.974-64.692 41.897v32.386l273.846 157.538q17.173 9.912 26.663 26.582 9.491 16.671 9.491 36.495v62.152q0 19.824-9.491 36.495-9.49 16.67-26.663 26.582L516.154-111.771q-17.203 9.846-36.217 9.846t-36.091-9.846L176.155-265.847q-17.173-9.912-26.663-26.582-9.491-16.671-9.491-36.495v-62.152q0-19.824 9.491-36.495 9.49-16.67 26.663-26.582l273.846-157.538Zm-6.155 364.537L200-387.461v58.537q0 3.078 1.539 5.962 1.538 2.885 4.615 4.808l267.692 154.692q3.077 1.923 6.154 1.923t6.154-1.923l267.692-154.692q3.077-1.923 4.615-4.808 1.539-2.884 1.539-5.962v-58.537L516.154-247.154q-17.203 9.847-36.217 9.847t-36.091-9.847Zm6.155-162.847V-542.77L250.46-427.691l223.386 128.846q3.077 1.924 6.154 1.924t6.154-1.924l223.001-128.846L509.999-542.77v132.769h-59.998ZM480-699.999q25 0 42.5-17.5t17.5-42.5q0-25-17.5-42.5t-42.5-17.5q-25 0-42.5 17.5t-17.5 42.5q0 25 17.5 42.5t42.5 17.5Zm-2.308 538.61Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

+2 -2
View File
@@ -18,11 +18,11 @@ Here is a set of instructions to help LLMs generate correct code that follows An
## Rules Files
Several editors, such as <a href="https://studio.firebase.google.com?utm_source=adev&utm_medium=website&utm_campaign=BUILD_WITH_AI_ANGULAR&utm_term=angular_devrel&utm_content=build_with_ai_angular_firebase_studio">Firebase Studio</a> have rules files useful for providing critical context to LLMs.
Several editors, such as <a href="https://studio.firebase.google.com?utm_source=adev&utm_medium=website&utm_campaign=BUILD_WITH_AI_ANGULAR&utm_term=angular_devrel&utm_content=build_with_ai_angular_firebase_studio" target="_blank">Firebase Studio</a> have rules files useful for providing critical context to LLMs.
| Environment/IDE | Rules File | Installation Instructions |
| :------------------- | :--------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Firebase Studio | <a download href="/assets/context/airules.md" target="_blank">airules.md</a> | <a href="https://firebase.google.com/docs/studio/set-up-gemini#custom-instructions">Configure `airules.md`</a> |
| Firebase Studio | <a download href="/assets/context/airules.md" target="_blank">airules.md</a> | <a href="https://firebase.google.com/docs/studio/set-up-gemini#custom-instructions" target="_blank">Configure `airules.md`</a> |
| Copilot powered IDEs | <a download="copilot-instructions.md" href="/assets/context/guidelines.md" target="_blank">copilot-instructions.md</a> | <a href="https://code.visualstudio.com/docs/copilot/copilot-customization#_custom-instructions" target="_blank">Configure `.github/copilot-instructions.md`</a> |
| Cursor | <a download href="/assets/context/angular-20.mdc" target="_blank">cursor.md</a> | <a href="https://docs.cursor.com/context/rules" target="_blank">Configure `cursorrules.md`</a> |
| JetBrains IDEs | <a download href="/assets/context/guidelines.md" target="_blank">guidelines.md</a> | <a href="https://www.jetbrains.com/help/junie/customize-guidelines.html" target="_blank">Configure `guidelines.md`</a> |
+3 -3
View File
@@ -59,7 +59,7 @@ Here are examples of how to build with Genkit and Angular:
Here is an example of how to build with Firebase AI Logic and Angular:
- [Firebase AI Logic x Angular Starter Kit](https://github.com/angular/examples/tree/main/vertex-ai-firebase-angular-example) - Use this starter-kit to build an e-commerce application with a chat agent that can perform tasks. Start here if you do not have experience building with Firebase AI Logic and Angular.
- [Firebase AI Logic x Angular Starter Kit](https://github.com/angular/examples/tree/main/firebase-ai-logic-angular-example) - Use this starter-kit to build an e-commerce application with a chat agent that can perform tasks. Start here if you do not have experience building with Firebase AI Logic and Angular.
This example includes an [in-depth video walkthrough explaining the functionality and demonstrates how to add new features](https://youtube.com/live/4vfDz2al_BI).
@@ -79,7 +79,7 @@ When connecting to model providers, it is important to keep your API secrets saf
Your application's architecture determines which AI APIs and tools to choose. Specifically, choose based on whether or not your application is client-side or server-side. Tools such as Firebase AI Logic provide a secure connection to the model APIs for client-side code. If you want to use a different API than Firebase AI Logic or prefer to use a different model provider, consider creating a proxy-server or even [Cloud Functions for Firebase](https://firebase.google.com/docs/functions) to serve as a proxy and not expose your API keys.
For an example of connecting using a client-side app, see the code: [Firebase AI Logic Angular example repository](https://github.com/angular/examples/tree/main/vertex-ai-firebase-angular-example).
For an example of connecting using a client-side app, see the code: [Firebase AI Logic Angular example repository](https://github.com/angular/examples/tree/main/firebase-ai-logic-angular-example).
For server-side connections to model APIs that require API keys, prefer using a secrets manager or environment variable, not `environments.ts`. You should follow standard best practices for securing API keys and credentials. Firebase now provides a new secrets manager with the latest updates from Firebase App Hosting. To learn more, [check out the official documentation](https://firebase.google.com/docs/app-hosting/configure).
@@ -91,7 +91,7 @@ If you want to build agentic workflows, where agents are able to act and use too
Tool calling further enhances your web apps by expanding your AI integration further than a question and answer style chat bot. In fact, you can empower your model to request function calls using the function calling API of your model provider. The available tools can be used to perform more complex actions within the context of your application.
In the [e-commerce example](https://github.com/angular/examples/blob/main/vertex-ai-firebase-angular-example/src/app/ai.service.ts#L88) of the [Angular examples repository](https://github.com/angular/examples), the LLM requests to make calls to functions for inventory in order to gain the necessary context to perform more complex tasks such as calculating how much a group of items in the store will cost. The scope of the available API is up to you as a developer just as is whether or not to call a function requested by the LLM. You remain in control of the flow of execution. You can expose specific functions of a service for example but not all functions of that service.
In the [e-commerce example](https://github.com/angular/examples/blob/main/firebase-ai-logic-angular-example/src/app/ai.service.ts#L88) of the [Angular examples repository](https://github.com/angular/examples), the LLM requests to make calls to functions for inventory in order to gain the necessary context to perform more complex tasks such as calculating how much a group of items in the store will cost. The scope of the available API is up to you as a developer just as is whether or not to call a function requested by the LLM. You remain in control of the flow of execution. You can expose specific functions of a service for example but not all functions of that service.
### Handling non-deterministic responses
+1 -1
View File
@@ -1,4 +1,4 @@
{
"branchName": "refs/heads/21.2.x",
"sha": "0707c69d63e9962bab077dbc45770c25c8d26b7c"
"sha": "887bb70d938ed041e4d1d61f6683c995cb91709c"
}
+5 -5
View File
@@ -255,7 +255,7 @@
"readonly",
"input"
],
"description": "The value of the menu item, used as the default aria-label",
"description": "The value of the menu item.",
"jsdocTags": [],
"inputAlias": "value",
"isRequiredInput": true
@@ -413,7 +413,7 @@
"name": "V"
}
],
"description": "An item in a Menu.\n\n`ngMenuItem` directives can be used in `ngMenu` and `ngMenuBar` to represent a choice\nor action a user can take. They can also act as triggers for sub-menus.\n\n```html\n<div ngMenu (itemSelected)=\"doAction()\">\n <div ngMenuItem >Action Item</div>\n <div ngMenuItem [submenu]=\"anotherMenu\">Submenu Trigger</div>\n</div>\n```",
"description": "An item in a Menu.\n\n`ngMenuItem` directives can be used in `ngMenu` and `ngMenuBar` to represent a choice\nor action a user can take. They can also act as triggers for sub-menus.\n\n```html\n<div ngMenu (itemSelected)=\"doAction()\">\n <div ngMenuItem>Action Item</div>\n <div ngMenuItem [submenu]=\"anotherMenu\">Submenu Trigger</div>\n</div>\n```",
"jsdocTags": [
{
"name": "developerPreview",
@@ -428,7 +428,7 @@
"comment": "[MenuBar](guide/aria/menubar)"
}
],
"rawComment": "/**\n * An item in a Menu.\n *\n * `ngMenuItem` directives can be used in `ngMenu` and `ngMenuBar` to represent a choice\n * or action a user can take. They can also act as triggers for sub-menus.\n *\n * ```html\n * <div ngMenu (itemSelected)=\"doAction()\">\n * <div ngMenuItem >Action Item</div>\n * <div ngMenuItem [submenu]=\"anotherMenu\">Submenu Trigger</div>\n * </div>\n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Menu](guide/aria/menu)\n * @see [MenuBar](guide/aria/menubar)\n */",
"rawComment": "/**\n * An item in a Menu.\n *\n * `ngMenuItem` directives can be used in `ngMenu` and `ngMenuBar` to represent a choice\n * or action a user can take. They can also act as triggers for sub-menus.\n *\n * ```html\n * <div ngMenu (itemSelected)=\"doAction()\">\n * <div ngMenuItem>Action Item</div>\n * <div ngMenuItem [submenu]=\"anotherMenu\">Submenu Trigger</div>\n * </div>\n * ```\n *\n * @developerPreview 21.0\n *\n * @see [Menu](guide/aria/menu)\n * @see [MenuBar](guide/aria/menubar)\n */",
"implements": [],
"isStandalone": true,
"selector": "[ngMenuItem]",
@@ -441,7 +441,7 @@
"source": {
"filePath": "/src/aria/menu/menu-item.ts",
"startLine": 34,
"endLine": 109
"endLine": 108
}
},
{
@@ -792,7 +792,7 @@
"source": {
"filePath": "/src/aria/menu/menu.ts",
"startLine": 58,
"endLine": 194
"endLine": 201
}
}
],
+3
View File
@@ -63,6 +63,9 @@ For example:
For full details of these and other tools, see the [Angular CDK accessibility overview](https://material.angular.dev/cdk/a11y/overview).
For custom-styled components that need reusable WAI-ARIA interaction patterns, [Angular Aria](guide/aria/overview) provides headless directives for patterns such as accordion, combobox, listbox, menu, tabs, and toolbar.
These directives handle keyboard interaction, ARIA attributes, focus management, and screen reader support while letting you provide the HTML structure and styling for your application.
### Augmenting native elements
Native HTML elements capture several standard interaction patterns that are important to accessibility.
+3 -3
View File
@@ -6,9 +6,9 @@ Keeping your Angular application up-to-date enables you to take advantage of lea
This document contains information and resources to help you keep your Angular applications and libraries up-to-date.
For information about our versioning policy and practices —including support and deprecation practices, as well as the release schedule— see [Angular versioning and releases](reference/releases 'Angular versioning and releases').
For information about our versioning policy and practices — including support and deprecation practices, as well as the release schedule — see [Angular versioning and releases](reference/releases 'Angular versioning and releases').
HELPFUL: If you are currently using AngularJS, see [Upgrading from AngularJS](https://angular.io/guide/upgrade 'Upgrading from Angular JS').
HELPFUL: If you are currently using AngularJS, see [Upgrading from AngularJS](https://angular.io/guide/upgrade 'Upgrading from AngularJS').
_AngularJS_ is the name for all v1.x versions of Angular.
## Getting notified of new releases
@@ -30,7 +30,7 @@ To check your application's version of Angular use the `ng version` command from
The most recent stable released version of Angular appears [on npm](https://www.npmjs.com/package/@angular/core 'Angular on npm') under "Version." For example, `16.2.4`.
You can also find the most current version of Angular by using the CLI command [`ng update`](cli/update).
By default, [`ng update`](cli/update)(without additional arguments) lists the updates that are available to you.
By default, [`ng update`](cli/update) (without additional arguments) lists the updates that are available to you.
## Updating your environment and apps
+1 -1
View File
@@ -1,4 +1,4 @@
{
"branchName": "refs/heads/21.2.x",
"sha": "d0d970927ed7daa3a7072ef211d4b85c46c30eef"
"sha": "4e2c69bca091e2218fd456afe797826f033a76f2"
}
+99 -99
View File
@@ -482,19 +482,6 @@
"endLine": 19
}
},
{
"name": "CDK_DROP_LIST_GROUP",
"type": "any",
"entryType": "constant",
"rawComment": "/**\n * Injection token that can be used to reference instances of `CdkDropListGroup`. It serves as\n * alternative token to the actual `CdkDropListGroup` class which could cause unnecessary\n * retention of the class and its directive metadata.\n */",
"description": "Injection token that can be used to reference instances of `CdkDropListGroup`. It serves as\nalternative token to the actual `CdkDropListGroup` class which could cause unnecessary\nretention of the class and its directive metadata.",
"jsdocTags": [],
"source": {
"filePath": "/src/cdk/drag-drop/directives/drop-list-group.ts",
"startLine": 16,
"endLine": 18
}
},
{
"name": "CdkDragRelease",
"entryType": "interface",
@@ -533,6 +520,19 @@
"endLine": 26
}
},
{
"name": "CDK_DROP_LIST_GROUP",
"type": "any",
"entryType": "constant",
"rawComment": "/**\n * Injection token that can be used to reference instances of `CdkDropListGroup`. It serves as\n * alternative token to the actual `CdkDropListGroup` class which could cause unnecessary\n * retention of the class and its directive metadata.\n */",
"description": "Injection token that can be used to reference instances of `CdkDropListGroup`. It serves as\nalternative token to the actual `CdkDropListGroup` class which could cause unnecessary\nretention of the class and its directive metadata.",
"jsdocTags": [],
"source": {
"filePath": "/src/cdk/drag-drop/directives/drop-list-group.ts",
"startLine": 17,
"endLine": 19
}
},
{
"name": "CDK_DRAG_PLACEHOLDER",
"type": "any",
@@ -559,70 +559,6 @@
"endLine": 25
}
},
{
"name": "CdkDropListGroup",
"isAbstract": false,
"entryType": "undecorated_class",
"members": [
{
"name": "disabled",
"type": "boolean",
"memberType": "property",
"memberTags": [],
"description": "Whether starting a dragging sequence from inside this group is disabled.",
"jsdocTags": []
},
{
"name": "ngOnDestroy",
"signatures": [
{
"name": "ngOnDestroy",
"entryType": "function",
"description": "",
"generics": [],
"isNewType": false,
"jsdocTags": [],
"params": [],
"rawComment": "",
"returnType": "void"
}
],
"implementation": {
"params": [],
"isNewType": false,
"returnType": "void",
"generics": [],
"name": "ngOnDestroy",
"description": "",
"entryType": "function",
"jsdocTags": [],
"rawComment": ""
},
"entryType": "function",
"description": "",
"jsdocTags": [],
"rawComment": "",
"memberType": "method",
"memberTags": []
}
],
"generics": [
{
"name": "T"
}
],
"description": "Declaratively connects sibling `cdkDropList` instances together. All of the `cdkDropList`\nelements that are placed inside a `cdkDropListGroup` will be connected to each other\nautomatically. Can be used as an alternative to the `cdkDropListConnectedTo` input\nfrom `cdkDropList`.",
"jsdocTags": [],
"rawComment": "/**\n * Declaratively connects sibling `cdkDropList` instances together. All of the `cdkDropList`\n * elements that are placed inside a `cdkDropListGroup` will be connected to each other\n * automatically. Can be used as an alternative to the `cdkDropListConnectedTo` input\n * from `cdkDropList`.\n */",
"implements": [
"OnDestroy"
],
"source": {
"filePath": "/src/cdk/drag-drop/directives/drop-list-group.ts",
"startLine": 26,
"endLine": 42
}
},
{
"name": "CdkDragPlaceholder",
"isAbstract": false,
@@ -706,6 +642,70 @@
"endLine": 44
}
},
{
"name": "CdkDropListGroup",
"isAbstract": false,
"entryType": "undecorated_class",
"members": [
{
"name": "disabled",
"type": "boolean",
"memberType": "property",
"memberTags": [],
"description": "Whether starting a dragging sequence from inside this group is disabled.",
"jsdocTags": []
},
{
"name": "ngOnDestroy",
"signatures": [
{
"name": "ngOnDestroy",
"entryType": "function",
"description": "",
"generics": [],
"isNewType": false,
"jsdocTags": [],
"params": [],
"rawComment": "",
"returnType": "void"
}
],
"implementation": {
"params": [],
"isNewType": false,
"returnType": "void",
"generics": [],
"name": "ngOnDestroy",
"description": "",
"entryType": "function",
"jsdocTags": [],
"rawComment": ""
},
"entryType": "function",
"description": "",
"jsdocTags": [],
"rawComment": "",
"memberType": "method",
"memberTags": []
}
],
"generics": [
{
"name": "T"
}
],
"description": "Declaratively connects sibling `cdkDropList` instances together. All of the `cdkDropList`\nelements that are placed inside a `cdkDropListGroup` will be connected to each other\nautomatically. Can be used as an alternative to the `cdkDropListConnectedTo` input\nfrom `cdkDropList`.",
"jsdocTags": [],
"rawComment": "/**\n * Declaratively connects sibling `cdkDropList` instances together. All of the `cdkDropList`\n * elements that are placed inside a `cdkDropListGroup` will be connected to each other\n * automatically. Can be used as an alternative to the `cdkDropListConnectedTo` input\n * from `cdkDropList`.\n */",
"implements": [
"OnDestroy"
],
"source": {
"filePath": "/src/cdk/drag-drop/directives/drop-list-group.ts",
"startLine": 27,
"endLine": 43
}
},
{
"name": "CdkDragPreview",
"isAbstract": false,
@@ -6294,11 +6294,11 @@
"@angular/core"
],
[
"OnDestroy",
"Input",
"@angular/core"
],
[
"Input",
"OnDestroy",
"@angular/core"
],
[
@@ -6422,11 +6422,11 @@
"@angular/cdk/drag-drop"
],
[
"CDK_DROP_LIST_GROUP",
"CdkDragRelease",
"@angular/cdk/drag-drop"
],
[
"CdkDragRelease",
"CDK_DROP_LIST_GROUP",
"@angular/cdk/drag-drop"
],
[
@@ -6438,11 +6438,11 @@
"@angular/cdk/drag-drop"
],
[
"CdkDropListGroup",
"CdkDragPlaceholder",
"@angular/cdk/drag-drop"
],
[
"CdkDragPlaceholder",
"CdkDropListGroup",
"@angular/cdk/drag-drop"
],
[
@@ -6593,10 +6593,6 @@
"DropListOrientation",
"@angular/cdk/drag-drop"
],
[
"CDK_DROP_LIST_GROUP",
"@angular/cdk/drag-drop"
],
[
"CdkDragRelease",
"@angular/cdk/drag-drop"
@@ -6609,6 +6605,10 @@
"CdkDragRelease.event",
"@angular/cdk/drag-drop"
],
[
"CDK_DROP_LIST_GROUP",
"@angular/cdk/drag-drop"
],
[
"CDK_DRAG_PLACEHOLDER",
"@angular/cdk/drag-drop"
@@ -6617,18 +6617,6 @@
"CDK_DRAG_PREVIEW",
"@angular/cdk/drag-drop"
],
[
"CdkDropListGroup",
"@angular/cdk/drag-drop"
],
[
"CdkDropListGroup.disabled",
"@angular/cdk/drag-drop"
],
[
"CdkDropListGroup.ngOnDestroy",
"@angular/cdk/drag-drop"
],
[
"CdkDragPlaceholder",
"@angular/cdk/drag-drop"
@@ -6645,6 +6633,18 @@
"CdkDragPlaceholder.ngOnDestroy",
"@angular/cdk/drag-drop"
],
[
"CdkDropListGroup",
"@angular/cdk/drag-drop"
],
[
"CdkDropListGroup.disabled",
"@angular/cdk/drag-drop"
],
[
"CdkDropListGroup.ngOnDestroy",
"@angular/cdk/drag-drop"
],
[
"CdkDragPreview",
"@angular/cdk/drag-drop"
@@ -37,7 +37,7 @@ The `outputToObservable` function lets you create an RxJS observable from a comp
import {outputToObservable} from '@angular/core/rxjs-interop';
@Component(/*...*/)
class CustomSlider {
class CustomSlider {
valueChange = output<number>();
}
@@ -85,7 +85,7 @@ For example, an asset group that matches `/foo.js` should appear before one that
Each asset group specifies both a group of resources and a policy that governs them.
This policy determines when the resources are fetched and what happens when changes are detected.
Asset groups follow the Typescript interface shown here:
Asset groups follow the TypeScript interface shown here:
```ts
interface AssetGroup {
@@ -179,7 +179,7 @@ The first data group that matches the requested resource handles the request.
It is recommended that you put the more specific data groups higher in the list.
For example, a data group that matches `/api/foo.json` should appear before one that matches `/api/*.json`.
Data groups follow this Typescript interface:
Data groups follow this TypeScript interface:
```ts
export interface DataGroup {
@@ -283,7 +283,7 @@ When the service worker's request for `ngsw.json` returns a `404`, then the serv
<!-- vale Angular.Google_Acronyms = NO -->
A small script, `safety-worker.js`, is also included in the `@angular/service-worker` NPM package.
A small script, `safety-worker.js`, is also included in the `@angular/service-worker` npm package.
When loaded, it un-registers itself from the browser and removes the service worker caches.
This script can be used as a last resort to get rid of unwanted service workers already installed on client pages.
+1 -13
View File
@@ -22,16 +22,4 @@ Angular v21 is being delivered to you as a brand new release adventure. With mod
- Your first look at Signal Forms, our new streamlined, signal-based approach to forms in Angular
- Exciting new details about the Angular Aria package
<div style="display: block; width: 80%; margin: 0 auto; margin-top: 20px;">
<iframe
credentialless
width="560"
height="315"
src="https://www.youtube.com/embed/DDAHORVzQ5g?si=B9Uv5vXzIKJfcLGr"
title="YouTube video player"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
allowfullscreen
></iframe>
</div>
<docs-video src="https://www.youtube.com/embed/DDAHORVzQ5g?si=B9Uv5vXzIKJfcLGr" title="Angular v21 Developer Event"/>
+3 -3
View File
@@ -10,9 +10,9 @@ This sample comes from the Angular documentation's "[Example Angular Internation
> See the scripts in `package.json` for an explanation of these commands.
## Run in Stackblitz
## Run in StackBlitz
Stackblitz compiles and runs the English version by default.
StackBlitz compiles and runs the English version by default.
To see the example translate to French with Angular i18n:
@@ -24,4 +24,4 @@ To see the example translate to French with Angular i18n:
}
```
1. Click the "Fork" button in the stackblitz header. That makes a new copy for you with this change and re-runs the example in French.
1. Click the "Fork" button in the StackBlitz header. That makes a new copy for you with this change and re-runs the example in French.
@@ -52,7 +52,7 @@ export class ProfileEditorComponent {
// #enddocregion add-alias
// #docregion on-submit
onSubmit() {
// TODO: Use EventEmitter with form value
// TODO: Use output() with form value
console.warn(this.profileForm.value);
}
// #enddocregion on-submit
@@ -17,12 +17,12 @@ The functions that control complex animation sequences are:
## The query() function
Most complex animations rely on the `query()` function to find child elements and apply animations to them, basic examples of such are:
Most complex animations rely on the `query()` function to find child elements and apply animations to them. Basic examples include:
| Examples | Details |
| :------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `query()` followed by `animate()` | Used to query simple HTML elements and directly apply animations to them. |
| `query()` followed by `animateChild()` | Used to query child elements, which themselves have animations metadata applied to them and trigger such animation \(which would be otherwise be blocked by the current/parent element's animation\). |
| Examples | Details |
| :------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `query()` followed by `animate()` | Used to query simple HTML elements and directly apply animations to them. |
| `query()` followed by `animateChild()` | Used to query child elements, which themselves have animation metadata applied to them and trigger such animations \(which would otherwise be blocked by the current/parent element's animation\). |
The first argument of `query()` is a [css selector](https://developer.mozilla.org/docs/Web/CSS/CSS_Selectors) string which can also contain the following Angular-specific tokens:
@@ -43,7 +43,7 @@ You can also see an illustration of this in the animations example \(introduced
## Animate multiple elements using query() and stagger() functions
After having queried child elements via `query()`, the `stagger()` function lets you define a timing gap between each queried item that is animated and thus animates elements with a delay between them.
After querying child elements via `query()`, the `stagger()` function lets you define a timing gap between each item, animating elements with a delay between them.
The following example demonstrates how to use the `query()` and `stagger()` functions to animate a list \(of heroes\) adding each in sequence, with a slight delay, from top to bottom.
+6 -6
View File
@@ -4,7 +4,7 @@ CSS offers a robust set of tools for you to create beautiful and engaging animat
## How to write animations in native CSS
If you've never written any native CSS animations, there are a number of excellent guides to get you started. Here's a few of them:
If you've never written any native CSS animations, there are a number of excellent guides to get you started. Here are a few of them:
[MDN's CSS Animations guide](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animations/Using_CSS_animations)
[W3Schools CSS3 Animations guide](https://www.w3schools.com/css/css3_animations.asp)
[The Complete CSS Animations Tutorial](https://www.lambdatest.com/blog/css-animations-tutorial/)
@@ -14,7 +14,7 @@ and a couple of videos:
[Learn CSS Animation in 9 Minutes](https://www.youtube.com/watch?v=z2LQYsZhsFw)
[Net Ninja CSS Animation Tutorial Playlist](https://www.youtube.com/watch?v=jgw82b5Y2MU&list=PL4cUxeGkcC9iGYgmEd2dm3zAKzyCGDtM5)
Check some of these various guides and tutorials out, and then come back to this guide.
Check out some of these guides and tutorials, then come back to this guide.
## Creating Reusable Animations
@@ -28,7 +28,7 @@ Adding the class `animated-class` to an element would trigger the animation on t
### Animating State and Styles
You may want to animate between two different states, for example when an element is opened or closed. You can accomplish this by using CSS classes either using a keyframe animation or transition styling.
You may want to animate between two different states, for example when an element is opened or closed. You can accomplish this by using CSS classes, either with a keyframe animation or transition styling.
<docs-code header="animations.css" path="adev/src/content/examples/animations/src/app/animations.css" region="animation-states"/>
@@ -38,7 +38,7 @@ You can see similar examples in the template guide for [animating styles directl
### Transitions, Timing, and Easing
Animating often requires adjusting timing, delays and easeing behaviors. This can be done using several css properties or shorthand properties.
Animating often requires adjusting timing, delays, and easing behaviors. This can be done using several css properties or shorthand properties.
Specify `animation-duration`, `animation-delay`, and `animation-timing-function` for a keyframe animation in CSS, or alternatively use the `animation` shorthand property.
@@ -62,7 +62,7 @@ Animations can be triggered by toggling CSS styles or classes. Once a class is p
### Animating Auto Height
You can use css-grid to animate to auto height.
You can use CSS Grid to animate to auto height.
<docs-code-multifile preview path="adev/src/content/examples/animations/src/app/native-css/auto-height.ts">
<docs-code header="auto-height.ts" path="adev/src/content/examples/animations/src/app/native-css/auto-height.ts" />
@@ -70,7 +70,7 @@ You can use css-grid to animate to auto height.
<docs-code header="auto-height.css" path="adev/src/content/examples/animations/src/app/native-css/auto-height.css" />
</docs-code-multifile>
If you don't have to worry about supporting all browsers, you can also check out `calc-size()`, which is the true solution to animating auto height. See [MDN's docs](https://developer.mozilla.org/en-US/docs/Web/CSS/calc-size) and (this tutorial)[https://frontendmasters.com/blog/one-of-the-boss-battles-of-css-is-almost-won-transitioning-to-auto/] for more information.
If you don't have to worry about supporting all browsers, you can also check out `calc-size()`, which is the true solution to animating auto height. See [MDN's docs](https://developer.mozilla.org/en-US/docs/Web/CSS/calc-size) and [this tutorial](https://frontendmasters.com/blog/one-of-the-boss-battles-of-css-is-almost-won-transitioning-to-auto/) for more information.
### Animate entering and leaving a view
@@ -1,13 +1,13 @@
# Animating your applications with `animate.enter` and `animate.leave`
Well-designed animations can make your application more fun and straightforward to use, but they aren't just cosmetic.
Animations can improve your application and user experience in a number of ways:
Well-designed animations can make your application more intuitive and engaging, but they aren't just cosmetic.
Animations can improve your application and the user experience in a number of ways:
- Without animations, web page transitions can seem abrupt and jarring
- Motion greatly enhances the user experience, so animations give users a chance to detect the application's response to their actions
- Good animations can smoothly direct the user's attention throughout a workflow
Angular provides `animate.enter` and `animate.leave` to animate your application's elements. These two features apply enter and leave CSS classes at the appropriate times or call functions to apply animations from third party libraries. `animate.enter` and `animate.leave` are not directives. They are special API supported directly by the Angular compiler. They can be used on elements directly and can also be used as a host binding.
Angular provides `animate.enter` and `animate.leave` to animate your application's elements. These two features apply enter and leave CSS classes at the appropriate times or call functions to apply animations from third party libraries. `animate.enter` and `animate.leave` are not directives. They are special API supported directly by the Angular compiler. They can be used directly on elements and also as a host binding.
## `animate.enter`
@@ -19,7 +19,7 @@ You can use `animate.enter` to animate elements as they _enter_ the DOM. You can
<docs-code header="enter.css" path="adev/src/content/examples/animations/src/app/enter-and-leave/enter.css"/>
</docs-code-multifile>
When the animation completes, Angular removes the class or classes that you specified in `animate.enter` from the DOM. Animation classes are only be present while the animation is active.
When the animation completes, Angular removes the class or classes that you specified in `animate.enter` from the DOM. Animation classes are only present while the animation is active.
NOTE: When using multiple keyframe animations or transition properties on an element, Angular removes all classes only _after_ the longest animation has completed.
@@ -4,7 +4,7 @@ The `@angular/animations` package is deprecated as of v20.2, which also introduc
## How to write animations in native CSS
If you've never written any native CSS animations, there are a number of excellent guides to get you started. Here's a few of them:
If you've never written any native CSS animations, there are a number of excellent guides to get you started. Here are a few of them:
[MDN's CSS Animations guide](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animations/Using_CSS_animations)
[W3Schools CSS3 Animations guide](https://www.w3schools.com/css/css3_animations.asp)
[The Complete CSS Animations Tutorial](https://www.lambdatest.com/blog/css-animations-tutorial/)
@@ -14,7 +14,7 @@ and a couple of videos:
[Learn CSS Animation in 9 Minutes](https://www.youtube.com/watch?v=z2LQYsZhsFw)
[Net Ninja CSS Animation Tutorial Playlist](https://www.youtube.com/watch?v=jgw82b5Y2MU&list=PL4cUxeGkcC9iGYgmEd2dm3zAKzyCGDtM5)
Check some of these various guides and tutorials out, and then come back to this guide.
Check out some of these guides and tutorials, then come back to this guide.
## Creating Reusable Animations
@@ -40,7 +40,7 @@ The animations package allowed you to define various states using the [`state()`
<docs-code header="open-close.ts" path="adev/src/content/examples/animations/src/app/open-close.ts" region="state1"/>
This same behavior can be accomplished natively by using CSS classes either using a keyframe animation or transition styling.
This same behavior can be accomplished natively by using CSS classes, either with a keyframe animation or transition styling.
#### With Native CSS
@@ -102,7 +102,7 @@ The animations package offers the ability to animate things that have been histo
<docs-code header="auto-height.css" path="adev/src/content/examples/animations/src/app/animations-package/auto-height.css" />
</docs-code-multifile>
You can use css-grid to animate to auto height.
You can use CSS Grid to animate to auto height.
#### With Native CSS
@@ -2,9 +2,9 @@
IMPORTANT: The `@angular/animations` package is now deprecated. The Angular team recommends using native CSS with `animate.enter` and `animate.leave` for animations for all new code. Learn more at the new enter and leave [animation guide](guide/animations). Also see [Migrating away from Angular's Animations package](guide/animations/migration) to learn how you can start migrating to pure CSS animations in your apps.
Animation provides the illusion of motion: HTML elements change styling over time.
Well-designed animations can make your application more fun and straightforward to use, but they aren't just cosmetic.
Animations can improve your application and user experience in a number of ways:
Animation provides the illusion of motion: HTML elements change styles over time.
Well-designed animations can make your application more intuitive and engaging, but they aren't just cosmetic.
Animations can improve your application and the user experience in a number of ways:
- Without animations, web page transitions can seem abrupt and jarring
- Motion greatly enhances the user experience, so animations give users a chance to detect the application's response to their actions
@@ -70,7 +70,7 @@ You put the trigger that defines an animation within the `animations` metadata p
Let's animate a transition that changes a single HTML element from one state to another.
For example, you can specify that a button displays either **Open** or **Closed** based on the user's last action.
When the button is in the `open` state, it's visible and yellow.
When it's the `closed` state, it's translucent and blue.
When it's in the `closed` state, it's translucent and blue.
In HTML, these attributes are set using ordinary CSS styles such as color and opacity.
In Angular, use the `style()` function to specify a set of CSS styles for use with animations.
@@ -2,12 +2,12 @@
IMPORTANT: The `@angular/animations` package is now deprecated. The Angular team recommends using native CSS with `animate.enter` and `animate.leave` for animations for all new code. Learn more at the new enter and leave [animation guide](guide/animations). Also see [Migrating away from Angular's Animations package](guide/animations/migration) to learn how you can start migrating to pure CSS animations in your apps.
This topic provides some examples of how to create reusable animations.
This topic provides examples of how to create reusable animations.
## Create reusable animations
To create a reusable animation, use the [`animation()`](api/animations/animation) function to define an animation in a separate `.ts` file and declare this animation definition as a `const` export variable.
You can then import and reuse this animation in any of your application components using the [`useAnimation()`](api/animations/useAnimation) function.
You can then import and reuse this animation in your application components using the [`useAnimation()`](api/animations/useAnimation) function.
<docs-code header="animations.ts" path="adev/src/content/examples/animations/src/app/animations.1.ts" region="animation-const"/>
@@ -20,7 +20,7 @@ For example, the following snippet exports the animation `trigger`.
<docs-code header="animations.1.ts" path="adev/src/content/examples/animations/src/app/animations.1.ts" region="trigger-const"/>
From this point, you can import reusable animation variables in your component class.
From this point, you can import reusable animation variables into your component class.
For example, the following code snippet imports the `transitionAnimation` variable and uses it via the `useAnimation()` function.
<docs-code header="open-close.ts" path="adev/src/content/examples/animations/src/app/open-close.3.ts" region="reusable"/>
@@ -2,7 +2,7 @@
IMPORTANT: The `@angular/animations` package is now deprecated. The Angular team recommends using native CSS with `animate.enter` and `animate.leave` for animations for all new code. Learn more at the new enter and leave [animation guide](guide/animations). Also see [Migrating away from Angular's Animations package](guide/animations/migration) to learn how you can start migrating to pure CSS animations in your apps.
This guide goes into depth on special transition states such as the `*` wildcard and `void`. It shows how these special states are used for elements entering and leaving a view.
This guide goes into depth on special transition states such as the `*` wildcard and `void`. It also shows how these states are used for elements entering and leaving a view.
This section also explores multiple animation triggers, animation callbacks, and sequence-based animation using keyframes.
## Predefined states and wildcard matching
@@ -99,7 +99,7 @@ Use the aliases `:enter` and `:leave` to target HTML elements that are inserted
The `:enter` transition runs when any `*ngIf` or `*ngFor` views are placed on the page, and `:leave` runs when those views are removed from the page.
IMPORTANT: Entering/leaving behaviors can sometime be confusing.
IMPORTANT: Entering/leaving behaviors can sometimes be confusing.
As a rule of thumb consider that any element being added to the DOM by Angular passes via the `:enter` transition. Only elements being directly removed from the DOM by Angular pass via the `:leave` transition. For example, an element's view is removed from the DOM because its parent is being removed from the DOM.
This example has a special trigger for the enter and leave animation called `myInsertRemoveTrigger`.
@@ -239,7 +239,7 @@ You can combine keyframes with `duration`, `delay`, and `easing` within a single
### Keyframes with a pulsation
Use keyframes to create a pulse effect in your animations by defining styles at specific offset throughout the animation.
Use keyframes to create a pulse effect in your animations by defining styles at specific offsets throughout the animation.
Here's an example of using keyframes to create a pulse effect:
@@ -254,7 +254,7 @@ The code snippet for this animation might look like this.
### Animatable properties and units
Angular animations support builds on top of web animations, so you can animate any property that the browser considers animatable.
Angular animations are built on top of web animations, so you can animate any property that the browser considers animatable.
This includes positions, sizes, transforms, colors, borders, and more.
The W3C maintains a list of animatable properties on its [CSS Transitions](https://www.w3.org/TR/css-transitions-1) page.
+1 -1
View File
@@ -8,7 +8,7 @@
## Overview
The manubar is a horizontal navigation bar that provides persistent access to application menus. Menubars organize commands into logical categories like File, Edit, and View, helping users discover and execute application features through keyboard or mouse interaction.
The menubar is a horizontal navigation bar that provides persistent access to application menus. Menubars organize commands into logical categories like File, Edit, and View, helping users discover and execute application features through keyboard or mouse interaction.
<docs-tab-group>
<docs-tab label="Basic">

Some files were not shown because too many files have changed in this diff Show More