Compare commits

..

206 Commits

Author SHA1 Message Date
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
Pawel Kozlowski 50761c8be4 release: cut the v21.2.7 release 2026-04-01 20:36:00 +02:00
Matthieu Riegler 8d56b17b95 ci: exclude test files from primitives review
Those files aren't synced into G3 and shouldn't require external reviews

(cherry picked from commit bf8105ef76)
2026-04-01 20:27:31 +02:00
Angular Robot 4ad6d582d2 build: update cross-repo angular dependencies to v21.2.6
See associated pull request for more information.
2026-04-01 18:25:52 +02:00
Angular Robot f363ab41f1 build: update cross-repo angular dependencies to 616a50d
See associated pull request for more information.
2026-04-01 13:21:58 +02:00
Andrew Scott 0960592d3d fix(router): pass outlet context to split to fix empty path named outlets
The `split` helper function in `packages/router/src/utils/config_matching.ts` was blind to the current outlet being processed. When encountering an empty path named outlet in the config, it would assume it needed to pull it in as a synthetic empty group, even if we were already in the process of resolving that very outlet!

When navigating to `/(secondary:component-copy)` with this config:

```typescript
{
  path: '',
  component: MainLayout,
  children: [
    { path: '', outlet: 'secondary', component: SecondaryComponent, children: [{path: 'component-copy'}] }
  ]
}
```

The router uses `MainLayout` as a pass-through and calls `split` on its children with segments `['component-copy']`.
`split` uses the `containsEmptyPathMatchesWithNamedOutlets` helper to determine if there are any candidate empty path named outlets to pull in. Because of this, it sees `{ path: '', outlet: 'secondary' }` and says: "Ah, an empty path named outlet! I must pull it in!"
Rather than falling through to standard segment matching, it returns `UrlSegmentGroup(segments: [], children: {secondary: emptyGroup})`.
The router then tries to process `primary` (with `[]` segments) and fails because the config only has `secondary`. It also tries to process `secondary` with the `emptyGroup`. While `{ path: '', outlet: 'secondary' }` matches the empty group, its child `{ path: 'component-copy' }` fails to match because the `emptyGroup` has no segments! So both branches fail, resulting in a `NoMatch` error for the entire navigation!

Pulling in empty path named outlets IS desired when they act as siblings to segments we are matching. This has worked before and continues to work!

```typescript
{
  path: 'a',
  children: [
    { path: 'b', component: ComponentB },
    { path: '', component: ComponentC, outlet: 'aux' }
  ]
}
```

When navigating to `a/b`, `split` sees segments `['b']` and the `aux` empty path. It pulls in `aux` so it gets instantiated alongside `b`. This is correct!

If we have a named outlet with a non-empty path under an empty path parent:

```typescript
{
  path: '',
  component: MainLayout,
  children: [
    { path: 'component-copy', outlet: 'secondary', component: ComponentE }
  ]
}
```

When we navigate to `/(secondary:component-copy)`:
- `split` uses `containsEmptyPathMatchesWithNamedOutlets` to see if there are any empty path named outlets. Since it only sees `path: 'component-copy'`, it returns `false`.
- It falls through to standard segment matching, which finds `component-copy` in the segments array and activates it flawlessly!

This worked perfectly before the fix because it didn't use `containsEmptyPathMatchesWithNamedOutlets`.

The fix passes the **current active outlet context** into `split`. If `split` finds an empty path named outlet that matches the outlet we are already processing, it ignores it as a pull-in candidate.

When evaluating `MainLayout` children for `secondary`:
- URL Segments left to process: `['component-copy']`
- Current Outlet: `secondary`
- `childConfig`: `[{ path: '', outlet: 'secondary' }]`

Previously, `split` saw the empty path and pulled it in as a synthetic empty group, breaking matching. Now, since `getOutlet(r) === outlet` (both are `secondary`), the fix ignores it. Instead of returning empty segments, it **falls through to standard segment matching**, which successfully find the `component-copy` segment!

When evaluating `ComponentA` children for `primary`:
- URL Segments left to process: `['b']`
- Current Outlet: `primary`
- `childConfig`: `[{ path: 'b' }, { path: '', outlet: 'aux' }]`

Since `getOutlet(aux) !== primary`, the fix **does not ignore it**. `split` pulls in `aux: emptyGroup` as a sibling, instantiating `ComponentC` alongside `ComponentB`. This preserves correct behavior for auxiliary outlets!

fixes #67708

(cherry picked from commit daa9b2a9d6)
2026-04-01 11:48:47 +02:00
Alan Agius d04ddd73df fix(core): prevent binding unsafe attributes on SVG animation elements (#67797)
SVG animation elements (`animate` and `set`) can be used to animate sensitive attributes like `href` or `xlink:href`. Binding to these animation attributes (like `to`, `from`, or `values`) with a sensitive target creates an XSS vector.

This change mitigates this risk by:
1. Classifying `to`, `from`, and `values` on `<animate>` and `<set>` elements as `ATTRIBUTE_NO_BINDING` in the DOM security schema to prevent standard dynamic bindings.
2. Adding runtime validations in `ɵɵvalidateAttribute` to verify that `attributeName` is not a sensitive attribute (such as `href` or `xlink:href`) when processed by a set of `SECURITY_SENSITIVE_ATTRIBUTE_NAMES`. If it is, a runtime error `UNSAFE_ATTRIBUTE_BINDING` is thrown.
3. Adding regression tests in `integration_spec.ts` to ensure unsafe bindings throw an error while safe ones pass correctly.

PR Close #67797
2026-04-01 11:43:59 +02:00
Alan Agius b682c62873 fix(core): treat object[data] as resource URL context (#67797)
Previously, the `data` attribute of the `<object>` tag was being sanitized as a regular URL instead of a `ResourceURL`, which is security-sensitive.
This commit updates the runtime sanitization logic to correctly identify `object[data]` as a `ResourceURL` context. Additionally, the sanitizer lookup logic has been refactored to use a more efficient lookup map (`RESOURCE_MAP`) instead of multiple `Set` lookups, providing better performance and maintainability.

Added tests to verify the correct sanitization of `object[data]` and its behavior with trusted values.

PR Close #67797
2026-04-01 11:43:59 +02:00
Alan Agius fea25d1a60 fix(compiler): register SVG animation attributes in URL security context (#67797)
This change is a security hardening measure to prevent potentially unsafe attribute value manipulation through SVG animations. By mapping `animate|to`, `animate|from`, `animate|values`, and `set|to` to the `SecurityContext.URL`,  Angular will now automatically sanitize these attributes.

PR Close #67797
2026-04-01 11:43:59 +02:00
Kam f916531ee1 docs(docs-infra): improve angular-new-app skill
Consolidate duplicate persona intro, add commonly useful ng new flags
and missing generators to scaffolding guidelines.

(cherry picked from commit f257f54967)
2026-04-01 09:26:41 +02:00
tomer953 8fd896e99a fix(core): resolve component import by exact specifier in route lazy-loading schematic
Avoid substring matching on importClause.getText() which caused suffix collisions (e.g., BarComponent vs FooBarComponent). Use AST-based matching for default and named (including aliased) imports to reliably resolve the correct import path when generating loadComponent.

(cherry picked from commit 8fa6617352)
2026-04-01 09:24:13 +02:00
Kam 895c576d49 refactor(forms): use strict equality for pending status getter
The `pending` getter in `AbstractControl` used loose equality (`==`)
while all other status getters (`valid`, `invalid`, `disabled`) use
strict equality (`===`). Both sides are strings so behavior is
identical, but this inconsistency would fail strict linting rules.

(cherry picked from commit ef7679b7a5)
2026-03-31 13:52:00 +02:00
Angular Robot 5fd4f0f854 build: lock file maintenance
See associated pull request for more information.
2026-03-31 13:42:53 +02:00
Angular Robot 429513bed0 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-03-31 12:02:18 +02:00
Alan Agius f22d9a7098 docs: modernize CLI reference for build and test
Updates the Angular CLI reference to reflect current framework defaults:
- Changes the recommended application builder to @angular/build:application.
- Replaces Jasmine and Web Test Runner with Vitest as test runner examples.

(cherry picked from commit 1a6785874e)
2026-03-31 11:56:07 +02:00
Angular Robot cf7846bd62 build: update pnpm to v10.33.0
See associated pull request for more information.
2026-03-30 12:47:07 +02:00
Matthieu Riegler 9ea8cb6eea docs(docs-infra): sanitize markdown tooltip in Code editor
(cherry picked from commit 0f960a5514)
2026-03-30 12:16:16 +02:00
Alan Agius 3c41e74fdd fix(localize): validate locale in getOutputPathFn to prevent path traversal
The `localize-translate` CLI tool uses the `locale` field from translation files to expand the `{{LOCALE}}` placeholder in the output directory. It failed to sanitize `locale` input, allowing malicious translations to write files outside of the configured output directory.

This change mitigates this issue by combining.

Closes #67906

(cherry picked from commit 7871093822)
2026-03-30 12:15:31 +02:00
Shuaib Hasan Akib efbba945bc docs: align list formatting and improve emphasis
Aligns list formatting, replaces the “Helpful” block with an
IMPORTANT note for better visibility, and removes the separate
example file by inlining the single relevant line directly
in the documentation.
2026-03-30 11:12:29 +02:00
SkyZeroZx 7d54952e85 docs: update signal form tutorial
(cherry picked from commit 713eca74e7)
2026-03-30 11:11:28 +02:00
SkyZeroZx 94b1b530dc docs: update url from <base href>
(cherry picked from commit c6f73f4127)
2026-03-30 10:44:20 +02:00
marktechson 0c9c270552 docs: add agent skills documentation and new readme
(cherry picked from commit 86bbc9474c)
2026-03-27 11:39:57 -07:00
Georgi Serev 9b48cb2989 docs(docs-infra): introduce a custom UrlSerializer
The custom serializer should handle Adev-specific behavior like decoding encoded forward slash similarly to the app host.

(cherry picked from commit 9d79ec6866)
2026-03-27 17:17:55 +01:00
Andrei Chmelev bba5ed8e64 fix(compiler-cli): prevent recursive scope checks for invalid NgModule imports
Avoid recursive local scope lookups when invalid NgModule imports create import cycles.

(cherry picked from commit fcd0bb0db8)
2026-03-27 16:10:08 +01:00
Angular Robot 56c872dacc docs: update cross-repo adev docs
Updated Angular adev cross repo docs files.
2026-03-27 14:13:13 +01:00
Angular Robot c8902deb00 build: update all github actions
See associated pull request for more information.
2026-03-27 14:02:11 +01:00
Angular Robot 4b2008d0f2 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-03-27 14:00:33 +01:00
Andrew Scott b453c0ce67 refactor(language-service): Update getTcbNodesOfTemplateAtPosition to be usable without compiler (#67898)
updates getTcbNodesOfTemplateAtPosition to be usable without ngCompiler instance
so it can be shared with more compiler types

PR Close #67898
2026-03-26 21:32:20 +00:00
Andrew Scott a9ca90fc11 refactor: fix types extraction for private (#67898)
fixes types extraction for private entrypoint of language service

PR Close #67898
2026-03-26 21:32:20 +00:00
Jessica Janiuk 807cabd9e1 fix(dev-infra): improve PR review skill handling of git worktrees
The current PR review skill blindly attempts to checkout branches using the `gh` CLI.
This creates fatal errors if the branch being checked out is already tied to a different git worktree.
Additionally, the logic didn't give the user a clear way to enforce a remote code review when they are the author.

This change updates the `Execution Workflow` to prioritize an explicit user instruction to review remotely, and updates the `Local Code Review` instructions to enforce checking for worktree conflicts before executing local checkouts.
2026-03-26 18:45:39 +01:00
Doug Parker 13f050d220 test: construct local Date objects to fix timezone flakiness
Replaced testing constructions of `Date` objects from `formatDate` tests from plain ISO strings over to 'new Date(year, month, date)'.

Instantiating 'new Date("2024-01-01")' parses the string strictly as UTC midnight ("2024-01-01T00:00:00.000Z"). When local operations execute (such as calculating `getThursdayThisIsoWeek` boundaries), the UTC date shifts relative to the executing machine's timezone. For example, in PST (GMT-8), that date translates exactly to 'December 31st 16:00:00', pushing week boundaries backwards.

By wrapping date constructs explicitly as 'new Date(2024, 0, 1)', it natively guarantees local midnight execution and prevents boundaries shifting on global CI Remote Build Execution (RBE) workers.

Example (from a machine in PST):

```javascript
> new Date('2024-01-01')
Sun Dec 31 2023 16:00:00 GMT-0800 (Pacific Standard Time)
> new Date(2024, 0, 1)
Mon Jan 01 2024 00:00:00 GMT-0800 (Pacific Standard Time)
```

(cherry picked from commit 61ee183fa7)
2026-03-26 18:34:13 +01:00
Doug Parker d0cf2995a2 test: remove unsupported timezone from formatDate tests
Removes the 'America/New_York' timezone string test case from `formatDate` tests because the underlying `Date.parse` API does not support IANA timezone strings. This caused the timezone calculation to silently fall back to the local executing machine's timezone, leading to non-deterministic test flakiness on Remote Build Execution (RBE) workers operating in varying geographic locations.

(cherry picked from commit a1385ad977)
2026-03-26 18:34:13 +01:00
SkyZeroZx a35d572b22 docs: fix indentation that breaks roadmap rendering
(cherry picked from commit ecc616b94b)
2026-03-26 18:33:06 +01:00
cexbrayat 80fd08bcc8 docs: remove private utils from testing skills
(cherry picked from commit 9bb1e78969)
2026-03-26 18:29:24 +01:00
Leon Senft d8239c7836 release: cut the v21.2.6 release 2026-03-25 15:42:09 -07:00
Alan Agius c1579163b7 build: consolidate domino bundling in platform-server
Move the domino bundling logic and related shims into a centralized third_party directory within packages/platform-server. This avoids duplication of the bundling logic and ensures consistent shimming across the platform-server package and its entry points.

Following a conversation with OSS licensing, this change also includes the domino LICENSE file in the generated npm package to comply with licensing requirements for bundled third-party code.

```
├── fesm2022
│   ├── init.mjs
│   ├── init.mjs.map
│   ├── platform-server.mjs
│   ├── platform-server.mjs.map
│   ├── _server-chunk.mjs
│   ├── _server-chunk.mjs.map
│   ├── testing.mjs
│   └── testing.mjs.map
├── LICENSE
├── package.json
├── README.md
├── third_party
│   └── domino
│       ├── bundled-domino.d.ts
│       ├── bundled-domino.mjs
│       ├── bundled-domino.mjs.map
│       └── LICENSE
└── types
    ├── init.d.ts
    ├── platform-server.d.ts
    └── testing.d.ts
```

(cherry picked from commit b40d11eec4)
2026-03-25 13:31:10 -07:00
Angular Robot dbb15ebab2 build: update dependency node to v22.22.2
See associated pull request for more information.
2026-03-25 13:29:38 -07:00
Andrew Scott a6c49e0ea9 refactor(language-service): Export template target from API
allows template target to be used in other tooling

(cherry picked from commit eecfa4c909)
2026-03-25 13:07:47 -07:00
Michael Small 2c12339ab8 docs(forms): replace rxResource request w/ params
(cherry picked from commit 89629d8ac1)
2026-03-25 13:04:57 -07:00
Angular Robot a3048f231c build: update cross-repo angular dependencies
See associated pull request for more information.
2026-03-25 12:59:42 -07:00
Alan Agius ae2f2497d3 docs: reword allowedHosts security warning
Update the allowedHosts security warning in the security guide to remove the mention of DNS rebinding, which is primarily relevant for the dev server, and refocus on host header injection and SSRF. Additionally, clarify that allowing all hosts with "*" is only appropriate when header validation is handled by an external layer.

(cherry picked from commit 6f20fb513c)
2026-03-25 12:56:46 -07:00
Alan Agius 2c7d3b5704 build: re-sync file
Fixes patch CI due to missing synced file.
2026-03-25 10:53:09 +01:00
guidettj 6bd2121817 docs: fix incorrect getHarnesses() method to getAllHarnesses and broken link
(cherry picked from commit 050b14bd7f)
2026-03-24 15:35:36 -07:00
SkyZeroZx e8c047bd8d docs: update defer section to Markdown and fix absolute URL to relative in adev
(cherry picked from commit 098553bfe4)
2026-03-24 15:35:02 -07:00
hawkgs a0ca5a50b2 docs(docs-infra): redefine /guide/pipes redirect
Update the definition of the `/guide/pipes` redirect in order to avoid breaking the `a.dev/guide` redirect to Not Found page.

(cherry picked from commit 78c2d0aa19)
2026-03-24 15:17:15 -07:00
Fausto Davila 1cdb322600 docs: fix typo in end-to-end.md regarding e2e command
(cherry picked from commit c9f7a7f649)
2026-03-24 15:15:14 -07:00
Jens Kuehlers c9585b7159 docs: update release date to not clash with Google I/O 2026
(cherry picked from commit b4ae9cf040)
2026-03-24 15:14:38 -07:00
Angular Robot 238b07fe90 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-03-24 15:13:56 -07:00
Angular Robot 3b3ab8b626 build: lock file maintenance
See associated pull request for more information.
2026-03-24 15:08:55 -07:00
weedorflow e731bbdd14 docs: clarify size budget configuration
(cherry picked from commit 37b1c5d0fe)
2026-03-24 14:49:38 -07:00
Aleksander Bodurri c7a18924a6 fix(devtools): fix incorrect logic in destroy function for ChromeMessageBus
Destroy uses window.removeEventListener but the ChromeMessageBus doesn't actually depend on a window object.

In practice this code is unlikely to ever be reached. If a tab is closed the entire context script JS process is killed so this is not an bug that would be very common or even reachable. That being said for correctness this should not be using window.

(cherry picked from commit 36edf4870f)
2026-03-23 14:51:32 -07:00
Joey Perrott 244e0b0b59 ci: correct capitalization of MarkTechson in pullapprove
Correct the capitalization of MarkTechson's username to match their GitHub profile, ensuring proper evaluation by PullApprove.

(cherry picked from commit 543753f551)
2026-03-23 14:50:23 -07:00
Alan Agius 9bfe1a6065 docs: remove redundant ng command from npx instructions in Angular Developer skill
(cherry picked from commit b89af3c8da)
2026-03-23 14:49:30 -07:00
Modeste ASSIONGBON b658accf1f docs: fix js doc of signal forms ignoreValidators option.
(cherry picked from commit 2615f35da9)
2026-03-23 11:21:21 -07:00
Pushkar-Mahajan4 9be5e2c211 docs: fix typo in Input decorator
(cherry picked from commit 1128e78913)
2026-03-23 11:19:51 -07:00
SkyZeroZx 8818095a82 docs(docs-infra): fix aria-label parsing breaking headings
Strip all HTML tags and escape quotes in aria-label to avoid breakage

(cherry picked from commit 6d2eed61a1)
2026-03-23 10:33:19 -07:00
splincode 80c85ec8a5 refactor(devtools): improve test descriptions by fixing spelling
Fixed misspellings in test descriptions in devtools/projects/shell-browser/src/app/tab_manager_spec.ts: recieved/recieves → received/receives in three it(...) titles.

(cherry picked from commit 25dad82e43)
2026-03-23 10:32:44 -07:00
splincode fb7a10fb18 docs: fix typos in ssr-benchmarks README.md
Corrected typos in modules/ssr-benchmarks/README.md: mesure→measure, Struture→Structure, render→renders, benckmark→benchmark, capitalized DevTools, and clarified the flame chart instruction.

(cherry picked from commit 13c198ae63)
2026-03-23 10:27:43 -07:00
SkyZeroZx 1af0db5742 docs(docs-infra): replace non-interactive buttons with spans
These elements are not interactive, so using <button> is misleading for accessibility
(screen readers and keyboard navigation expect an action).

(cherry picked from commit ad3991cd0b)
2026-03-23 10:24:13 -07:00
Alan Agius a821e06e33 ci: support publishing dev-skills to snapshot repository
Updates the snapshot publishing script to package the contents of
`skills/dev-skills` and publish them to the `angular/skills`
repository.

The changes include:
- Ensuring the script always runs from the repository root.
- Updating `publishRepo` to support an optional third argument for
  specifying a custom repository name (defaulting to `-builds`).
- Implementing `publishDevSkills` to package individual skills at the
  root of the snapshot repository.

(cherry picked from commit 293ac66a9f)
2026-03-23 09:49:15 -07:00
Matthieu Riegler a21be36e15 refactor: prepare for required changeDetection prop on G3.
We'll make this a G3 only change to prevent backsliding during the transition period.

(cherry picked from commit d2054c7c1a)
2026-03-20 15:52:42 -07:00
Angular Robot 7aaa8b9b39 docs: update cross-repo adev docs
Updated Angular adev cross repo docs files.
2026-03-20 15:39:47 -07:00
Angular Robot d0481860bf build: update cross-repo angular dependencies
See associated pull request for more information.
2026-03-20 15:23:08 -07:00
Matt Lewis 880a57d4b3 fix(compiler): prevent shimCssText from adding extra blank lines per CSS comment
The comment placeholder restoration in `shimCssText` appended an unconditional
`+ '\n'` to each non-hash comment replacement. Because `_commentRe` does not
consume the newline that follows a comment in the source, that newline already
remains in `cssText`. The extra `'\n'` was therefore inserted on top of the
existing one, shifting every line after each comment down by one. In files with
many comments (e.g. large SCSS preambles) this shifts all subsequent CSS rules
far enough that the CSS sourcemap — generated before `shimCssText` runs —
points to completely wrong source locations in browser DevTools.

The fix is to drop the `+ '\n'`; internal newlines within a multi-line comment
are still preserved via `_newLinesRe`, and the trailing newline that follows the
comment in `cssText` is already present without any extra injection.

(cherry picked from commit 5a712d42d1)
2026-03-20 15:17:35 -07:00
Angular Robot 1ba89d1128 build: lock file maintenance
See associated pull request for more information.
2026-03-20 15:14:25 -07:00
Matthieu Riegler 831746eff1 docs: Add callout about the Component interface.
(cherry picked from commit e01573fbca)
2026-03-19 16:14:22 -07:00
Jaime Burgos 9182b62b96 docs(forms): add section on managing validators dynamically in form validation guide
(cherry picked from commit eb53392b10)
2026-03-19 16:07:54 -07:00
Angular Robot 4d2a9394c3 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-03-19 16:03:49 -07:00
Jessica Janiuk ad0156e056 fix(core): fixes a regression with animate.leave and reordering
PATCH PR for #67765

This fixes a regression bug that resulted in reordered elements not getting properly removed from the DOM. Reused nodes were not being cleared out in this situation.

fixes: #67728
2026-03-19 15:52:35 -07:00
Angular Robot aa3220a4b5 build: update dependency aspect_rules_ts to v3.8.7
See associated pull request for more information.
2026-03-19 15:23:54 -07:00
SkyZeroZx b4ab6ba2e8 fix(common): avoid redundant image fetch on destroy with auto sizes
prevents browsers from re-fetch image during DOM teardown
when using `sizes="auto"` with lazy loading.

Fixes angular#67055

(cherry picked from commit c1312da183)
2026-03-19 15:21:20 -07:00
aparziale 73d6b01b47 fix(migrations): inject migration not work in multi-project workspace with option path
Fix inject migration in multi-project workspace. The inject migration doesn't work when targeting one of the projects due to either not finding any files in other projects or considering them external thus it throws a SchematicsException

Fixes: #66074
(cherry picked from commit a73b4b7c30)
2026-03-19 15:20:46 -07:00
Angular Robot 3838554503 build: update cross-repo angular dependencies to v21.2.3
See associated pull request for more information.
2026-03-19 15:04:08 -07:00
Angular Robot 1148c93834 build: update all github actions
See associated pull request for more information.
2026-03-19 15:02:54 -07:00
Matthieu Riegler 9f92312cde docs(docs-infra): fix deprecation style for light theme
(cherry picked from commit cc07d46e49)
2026-03-19 15:00:31 -07:00
Doug Parker 04b16dee8f refactor: ignore pnpm-lock.yaml in Gemini code reviews
It can't give much useful feedback so this just introduces noise into Renovate PRs.

(cherry picked from commit 12ea6efc9d)
2026-03-19 14:56:00 -07:00
Matthew Beck 57ce2b64ef release: cut the v21.2.5 release 2026-03-18 17:33:53 -06:00
Angular Robot 5393d8ea2c docs: update cross-repo adev docs
Updated Angular adev cross repo docs files.
2026-03-18 14:08:01 -06:00
SkyZeroZx 675d4fb489 docs: update import and note formatting in guides
(cherry picked from commit a8bf957086)
2026-03-18 13:04:21 -07:00
Jessica Janiuk 851ef77318 Revert "refactor(core): Ensure determineLongestAnimation is run synchronously after style applies"
This reverts commit 318ade062e.

(cherry picked from commit 890c97301f)
2026-03-18 10:30:40 -07:00
Yenya030 5800f17e19 refactor(service-worker): remove unnecessary cast in mock redirect check
Use the typed Response.redirected property directly in the service-worker test mock instead of casting to any.

(cherry picked from commit 57a07d0058)
2026-03-18 10:26:26 -07:00
Yenya030 e19150d2b5 fix(service-worker): preserve redirect policy on reconstructed asset requests
Preserve the redirect mode when rebuilding asset requests in newRequestWithMetadata(). This keeps explicit redirect:error semantics intact across service-worker redirect handling.

Update the worker test mocks to model redirect defaults correctly and add focused regression coverage for redirected lazy assets with redirect:error.

(cherry picked from commit 07abfbcc6c)
2026-03-18 10:26:25 -07:00
SkyZeroZx 0e496c1d85 docs(docs-infra): improve semantic HTML in update guide
Improves update guide's semantic HTML
2026-03-18 09:42:32 -06:00
kirjs 737777ef72 docs: add Angular Developer Skill updates
Includes:

- Imports and setup instructions

- Validation examples (sync, async, conditional)

- FieldState vs FormField distinction

- Common pitfalls and best practices

- Full-featured example application

(cherry picked from commit ed150e52d1)
2026-03-17 17:39:44 -07:00
marktechson fa26e7271f docs: adds angular-developer skill
Adds a new agent skill focused on providing fundamentals to coding agents and adhering
to modern Angular code including Signals, Signal Forms and other latest updates.

(cherry picked from commit 8291b16a36)
2026-03-17 17:39:43 -07:00
aparziale 076d41c3f6 fix(migrations): prevent trailing comma syntax errors after removing NgStyle
This fixes an issue where when removing NgStyle 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 730684b9ce)
2026-03-17 17:01:28 -07:00
JoostK a8f80c1f99 refactor(core): declare explicit reactive node prototypes types
These type annotations allow TS to associate the object's properties
with their corresponding declaration in the interfaces, enabling
much better code navigation. For example, "Find all implementations"
for `ReactiveNode.producerRecomputeValue` now finds the implementation
in `COMPUTED_NODE` and `LINKED_SIGNAL_NODE`.

(cherry picked from commit 1eaf92077f)
2026-03-17 13:00:15 -07:00
JoostK bf948be4c2 fix(core): run linked signal equality check without reactive consumer
This commit ports the changes in #55818 from `computed` to `linkedSignal`,
which duplicates the core logic to recompute the downstream value for an
upstream change.

(cherry picked from commit 523d69a768)
2026-03-17 12:59:42 -07:00
yogeshwaran-c 23ea431c4e fix(compiler): parse named HTML entities containing digits
The lexer's isNamedEntityEnd function stopped scanning entity names
when encountering a digit character, causing 24 valid HTML named
entities with digits in their names (e.g. &sup1;, &frac12;, &blk34;)
to be treated as plain text instead of decoded to their corresponding
Unicode characters.

Fixes #51323

(cherry picked from commit 75560ce43d)
2026-03-17 12:54:45 -07:00
Alan Agius 2fb767b139 build: remove redundant Bazel lockfile update workarounds
Renovate now natively supports updating Bazel lockfiles.

This change removes the `postUpgradeTasks` workaround in Renovate config

(cherry picked from commit a37dd59fc4)
2026-03-17 12:53:11 -07:00
Angular Robot b10e305ccc build: update cross-repo angular dependencies
See associated pull request for more information.
2026-03-17 13:52:37 -06:00
Alan Agius f0d19b88b1 docs: clarify X-Forwarded-Prefix header validation rules
(cherry picked from commit d0086e493b)
2026-03-17 12:50:15 -07:00
Kristiyan Kostadinov 67e0ba7e03 fix(compiler-cli): generic types not filled out correctly in type check block
Fixes a regression caused by the recent TCB changes where we moved the type parameter processing earlier in the pipeline and stopped properly accounting for the `TcbGenericContextBehavior`.

Fixes #67704.

(cherry picked from commit 9769560da7)
2026-03-17 12:21:31 -07:00
Angular Robot ca328eab8f docs: update cross-repo adev docs
Updated Angular adev cross repo docs files.
2026-03-16 14:47:18 -06:00
Andrew Scott faeeef678a feat(dev-infra): add PR review agent skill
This commit introduces an AI agent skill for reviewing pull requests
against the Angular repository. It establishes guidelines for ensuring
code cleanliness, performance, testing, API design, and payload size.

It supports performing reviews through both the GitHub CLI (remote) and
local editing, factoring in package-specific guidelines (e.g., router)
and prioritizing user approval before posting comments. By checking existing
comments first, the agent can avoid duplicate reviews.

This provides the AI agent with a reproducible workflow for providing
constructive, manual PR feedback.

(cherry picked from commit 2e34924647)
2026-03-16 13:25:51 -07:00
Angular Robot 4d786517d3 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-03-16 10:54:17 -06:00
Kristiyan Kostadinov 26c43d14ba fix(compiler-cli): escape template literal in TCB
Fixes a regression introduced by #67381 where we weren't escaping backticks in template literals.

Fixes #67675.

(cherry picked from commit 2bd708fb6b)
2026-03-16 09:47:29 -07:00
Max Millien 1890c3008b fix(core): clean up dehydrated views during HMR component replacement
During HMR, `recreateLView()` destroys the old LView and removes its
DOM nodes, but never cleans up dehydrated view DOM nodes stored in
`LContainer[DEHYDRATED_VIEWS]`. These are SSR-rendered DOM nodes
preserved by Angular's hydration system. When the new view renders,
both the old dehydrated DOM and the new DOM coexist, causing visible
duplication (e.g. `<app-shell>` header/footer appearing twice).

Call `cleanupLView` from the hydration cleanup module after
`destroyLView` and before `removeViewFromDOM` to remove any remaining
dehydrated DOM nodes before the replacement view is rendered.

Fixes #66503

(cherry picked from commit dc0446552a)
2026-03-16 09:04:11 -07:00
hawkgs cbb2266984 test(devtools): fix and update flaky component-tree tests
Due to the design of the `ng.getComponent` spy and a race condition where sometimes a `<script>` is added to the test DOM, the `getRootElements` tests used to fail sometimes because those `<script>`s were marked as roots which caused a distortion in the roots count checks. The commit addresses that and also adds an additional test for non-application root Angular components.

(cherry picked from commit 77d7378ffd)
2026-03-16 09:03:47 -07:00
SkyZeroZx ea4847aae5 docs(docs-infra): add spacing between reference option and description
Add a spacing class to `docs-reference-option` to prevent the text from appearing too close together.

(cherry picked from commit 3720b2de0f)
2026-03-16 09:00:50 -07:00
Akash Patel 5fa3052ed1 docs: update uninstallation commands for Karma packages
Added 'jasmine-core' to the list of packages to uninstall when migrating to Vitest.

(cherry picked from commit b9ff865b49)
2026-03-16 08:59:38 -07:00
SkyZeroZx 86718fbbe1 docs: fix type code block language
fix type code block language

(cherry picked from commit acd6f690bd)
2026-03-16 08:59:13 -07:00
guidettj de5fbb7deb docs: use lighter lodash isEqual import in equality example
(cherry picked from commit 7d483f28a4)
2026-03-16 08:58:44 -07:00
Angular Robot f39896da99 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-03-13 16:25:45 -06:00
ibrahim-hussien-dev 21cab20dae docs: combine multiple documentation typo fixes
(cherry picked from commit e127927807)
2026-03-13 12:10:59 -07:00
Angular Robot 2d28e9b392 build: update pnpm to v10.32.1
See associated pull request for more information.
2026-03-13 13:08:54 -06:00
Alan Agius 5b46a63c08 build: update rules_angular dependency commit hash
Update rules_angular to latest sha
2026-03-13 13:07:06 -06:00
Alan Agius 03fb3cd463 refactor: ensure modules with 'declare global' are correctly handled by Rollup
Explicitly adding an `export {}` to modules containing declare global fixes an issue where Rollup would incorrectly claim that the global variable is not defined in the emitted .d.ts files.

Needed to land the latest rules_angular.
2026-03-13 13:07:06 -06:00
SkyZeroZx 67a73192bf docs(docs-infra): remove tap highlight for API reference list filter
Prevents the default translucent highlight from appearing on touch devices when interacting with the API reference list filter.

(cherry picked from commit 7cd474d1d6)
2026-03-13 12:04:06 -07:00
Jessica Janiuk e5ed276f27 refactor(core): Ensure determineLongestAnimation is run synchronously after style applies
This adds a setTimeout, which guarantees that we call getAnimations one frame after a reflow is finished. This means getAnimations will return data, avoiding needing the expensive fallback of getComputedStyles. It also updates the cleanup to prevent a potential memory leak if the component is destroyed before the timeout runs.

(cherry picked from commit 318ade062e)
2026-03-13 12:03:15 -07:00
Kristiyan Kostadinov 334ae10168 fix(compiler): ensure generated code compiles
Initial pass to make sure some common cases produce code that compiles.
2026-03-13 12:53:47 -06:00
Kristiyan Kostadinov 1f2dc72e8a refactor(compiler-cli): update ast factories to account for type nodes
Updates the Babel and TypeScript AST factories to account to produce type nodes.
2026-03-13 12:53:47 -06:00
Kristiyan Kostadinov e3b0e5a0b3 refactor(compiler-cli): add type nodes to translator
Updates the translator and AST factories to account for type nodes.
2026-03-13 12:53:47 -06:00
Kristiyan Kostadinov 10b0dd94e9 refactor(compiler-cli): add generic for type nodes to AST factories
Updates the type factories and various usage sites to add a generic for type nodes.
2026-03-13 12:53:47 -06:00
Jessica Janiuk 6f41efc689 refactor(core): Update determineLongestAnimation to account for playback rate
This updates the determineLongestAnimation code to also calculate the playback rate in with the duration, which should also account for timing when testing with playback rates changed in devtools.

(cherry picked from commit 17d8a88ddc)
2026-03-12 16:36:54 -07:00
Kristiyan Kostadinov bfb8b177ac refactor(compiler-cli): remove more unused code
Cleans up some more usages of TS factory APIs and some unused functions.
2026-03-12 16:21:50 -06:00
Kristiyan Kostadinov 2feced366a refactor(compiler-cli): replace typescript usage in type reference emits
Replaces our usage of TypeScript APIs in several places that emit references to type nodes. Also deletes some unused code.
2026-03-12 16:21:50 -06:00
Alan Agius 5d755be01c refactor: ensure modules with 'declare global' are correctly handled by Rollup
Explicitly adding an `export {}` to modules containing `declare global` fixes an issue where Rollup would incorrectly claim that the `global` variable is not defined in the emitted `.d.ts` files.

Needed to land the latest `rules_angular`.

(cherry picked from commit bb628d8b50)
2026-03-12 13:58:03 -07:00
Matthew Beck 8017617fd9 release: cut the v21.2.4 release 2026-03-12 12:58:02 -06:00
Kristiyan Kostadinov ed2d324f9c fix(compiler): disallow translations of iframe src
Fixes that the compiler was allowing translations of `src` attributes in iframes which can be a security issue.

(cherry picked from commit 78dea55351)
2026-03-12 10:01:31 -07:00
Kristiyan Kostadinov d1dcd16c5b fix(core): sanitize translated form attributes
Fixes that we weren't sanitizing the `form` and `formaction` attributes when they're used together with translations.

(cherry picked from commit de0eb4c656)
2026-03-12 10:01:30 -07:00
Jessica Janiuk abbd8797bb fix(core): reverts "feat(core): add support for nested animations"
This reverts commit ea2016a6dc.

This reverts the support for nested animations due to the global scope of how nested animations were gathered.
This caused issues where on route navigations, all child nodes with animations would be queued and run before the navigation would occur.
We'll be revisiting the nested animations with a more tightened scope of when those leave animations will occur.

fixes: #67552
(cherry picked from commit 999c14eaab)
2026-03-12 09:58:07 -07:00
Angular Robot c06e3a0bb4 build: update bazel dependencies
See associated pull request for more information.
2026-03-11 14:41:33 -07:00
wokis 8ba2844da5 docs: document coverageExclude option for excluding files from coverage
(cherry picked from commit 3d25953e69)
2026-03-11 21:31:10 +00:00
Jon Snow 74698d58ec docs(forms): clarify zoneless change detection for reactive FormArray updates
Document that reactive forms model mutations such as FormArray.push() do not schedule component change detection in zoneless applications and show the recommended ways to notify Angular.

Fixes #65536

(cherry picked from commit 88235a0d12)
2026-03-11 14:13:27 -07:00
Andrew Scott a7e3d764e5 release: bump VSCode extension version to 21.2.3 (#67629) 2026-03-11 14:09:57 -07:00
Alan Agius 7907e982ee test: remove duplicate tests
These tests are duplicate and have been removed.
2026-03-11 13:35:26 -07:00
Alan Agius adda6c5c10 build: update aspect_rules_js to 3.0.2
This updates the major version of `aspect_rules_js`.
2026-03-11 13:35:26 -07:00
535 changed files with 27551 additions and 18546 deletions
-1
View File
@@ -1 +0,0 @@
../.gemini/skills
+133
View File
@@ -0,0 +1,133 @@
---
name: PR Review
description: Guidelines and tools for reviewing pull requests in the Angular repository.
---
# PR Review Guidelines
When reviewing a pull request for the `angular` repository, follow these essential guidelines to ensure high-quality contributions:
1. **Context & Ecosystem**:
- Keep in mind that this is the core Angular framework. Changes here can impact millions of developers.
- Be mindful of backwards compatibility. Breaking changes require strict approval processes and deprecation periods.
2. **Key Focus Areas**:
- **Comprehensive Reviews**: You **MUST always** perform a deep, comprehensive review of the _entire_ pull request. If the user asks you to look into a specific issue, file, or area of concern, you must investigate that specific area _in addition to_ reviewing the rest of the PR's substantive changes. Do not terminate your review after addressing only the user's focal point.
- **Package-Specific Guidelines**: Check if there are specific guidelines for the package being modified in the `reference/` directory (e.g., `reference/router.md`). Always prioritize these rules for their respective packages.
- **Commit Messages**: Evaluate the quality of commit messages. They should explain the _why_ behind the change, not just the _what_. Someone should be able to look at the commit history years from now and clearly understand the context and reasoning for the change.
- **Code Cleanliness**: Ensure the code is readable, maintainable, and follows Angular's project standards.
- **Performance**: Look out for code that might negatively impact runtime performance or bundle size, particularly in hot paths like change detection or rendering.
- **Testing**: Ensure all new logic has comprehensive tests, including edge cases. **Do NOT run tests locally** as part of your review process. CI handles this automatically, and running tests locally is redundant and inefficient.
- **API Design**: Ensure new public APIs are well-designed, consistent with existing APIs, and properly documented.
- **Payload Size**: Pay attention to the impact of changes on the final client payload size.
3. **Execution Workflow**:
Determine the appropriate review method. If the user explicitly asks for a `remote` or `local` review in their request, that takes precedence (e.g. "leave comments on the PR" implies `remote`). Otherwise, use the GitHub MCP or available scripts to determine if the review should be `local` or `remote`.
**Common Review Practices (Applies to both Local and Remote)**
- **Preparation & Checklist**:
- First, create a task list (e.g., in `task.md`) that you can easily reference containing **all** the review requirements from the "Key Focus Areas" section (Commit Messages, Performance, Testing, etc.), along with any specific review notes or requests from the user.
- Before doing an in-depth review, expand this list into more detailed items of what you plan to explore and verify in the PR.
- As you conduct the review, check off items in this list, adding your assessment or findings underneath each item.
- At the end of your review, refer back to the checklist to ensure every single requirement was completely verified.
- **Fetch PR Metadata Safely**: When you need to read the PR description or context, do NOT use `gh pr view <PR_NUMBER>` by itself, as its default GraphQL query may fail due to lack of `read:org` and `read:discussion` token scopes. Instead, use `read_url_content` on the PR URL or use `gh pr view <PR_NUMBER> --json title,body,state,author`.
- **Check Existing Comments First**: Before formulating feedback, use the GitHub MCP or available scripts to fetch existing comments on the PR. Review this feedback to avoid duplicate comments, and incorporate its insights into your own review process.
- **Constructive Feedback**: Provide clear, actionable, and polite feedback. Explain the _why_ behind your suggestions or edits. Do **NOT** leave inline comments purely to praise, agree with, or acknowledge a correct implementation detail, as this clutters the review. If you want to praise the PR, do so in the single general PR comment.
**A. Local Code Review (If the PR is owned by the author requesting the review)**
- **Checkout**: Check out the PR branch locally (if it doesn't already exist, fetch it). If checking out the branch fails due to a worktree claim (e.g. "fatal: '<branch>' is already used by worktree at '<path>'"), do the review in that directory.
- **Review & Edit**: Execute the review directly on the code. Instead of adding inline PR comments for suggestions, format the codebase or apply the edits directly to the files.
- **Feedback**: Summarize the review findings and the concrete changes you made in a message to the user, referencing the completed items from your checklist.
- **Do NOT Commit or Push**: Leave the changes uncommitted in the working directory so the user can easily review the pending edits locally. Let the user know the changes are ready for their review, but do not ask for approval to push.
- **Resolve Comments**: Once the user confirms the changes are good and should be committed/pushed, respond to the existing comments as 'resolved' using the GitHub MCP or available scripts.
**B. Remote Code Review (For all other PRs)**
- **Batching Comments (MCP Server - Preferred)**: If you have the GitHub MCP Server configured, you **MUST** follow this workflow to avoid spamming the author with multiple notifications:
1. Create a pending review using `mcp_github-mcp-server_pull_request_review_write` (method `create`).
2. Add your inline comments to the pending review using `mcp_github-mcp-server_add_comment_to_pending_review`.
3. Submit the review using `mcp_github-mcp-server_pull_request_review_write` (method `submit_pending`).
- **Batching Comments (Scripts - Fallback)**: If you do **NOT** have access to the GitHub MCP Server (e.g., specific MCP tools are missing from your context), fallback to using the provided scripts. Use `post_inline_comment.sh` to stage your comments locally. Once all comments are staged, you **MUST** call `submit_pr_review.sh` to publish them as a single batched review (and send a single notification). Try to keep comments minimal or use a general comment if you have many suggestions.
- **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.
- **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
The following tools are available for remote interactions. We prefer using standard **GitHub MCP Server** tools when available. If you do not have the MCP server set up, you **MUST** fallback to using the custom bash scripts.
### GitHub MCP Tools (Preferred)
- `mcp_github-mcp-server_pull_request_review_write`
- `mcp_github-mcp-server_add_comment_to_pending_review`
### Custom Bash Scripts (Fallback)
The following scripts are provided as fallbacks if the MCP server is not available. Note that they rely on the `gh` CLI being correctly installed and authenticated in the local environment.
### `determine_review_type.sh`
Determines whether to use the Local or Remote review workflow by checking if the currently authenticated GitHub user via the `gh` CLI matches the author of the pull request.
**Usage:**
```bash
.agent/skills/pr_review/scripts/determine_review_type.sh <PR_NUMBER>
```
### `get_pr_comments.sh`
Fetches all existing inline comments on a PR using the GitHub API. This is crucial for reviewing other contributors' feedback and avoiding duplicate comments. It outputs JSON containing the `id`, `path`, `line`, `body`, and `user` for each comment.
**Usage:**
```bash
.agent/skills/pr_review/scripts/get_pr_comments.sh <PR_NUMBER>
```
### `reply_pr_comment.sh`
Replies to an existing PR comment thread. This is useful for marking comments as resolved after addressing them in a local code review. Note that the `COMMENT_ID` must be the ID of the top-level comment in the thread.
**Usage:**
```bash
.agent/skills/pr_review/scripts/reply_pr_comment.sh <PR_NUMBER> <COMMENT_ID> <REPLY_BODY>
```
### `post_inline_comment.sh`
The GitHub CLI `gh pr review` command does not natively support adding inline comments to specific lines of code via its standard flags. This script wraps the GitHub API to stage comments locally. They will not be published until you call `submit_pr_review.sh`.
**Usage:**
```bash
.agent/skills/pr_review/scripts/post_inline_comment.sh <PR_NUMBER> <FILE_PATH> <LINE_NUMBER> <COMMENT_BODY>
```
**Example:**
```bash
.agent/skills/pr_review/scripts/post_inline_comment.sh 12345 "packages/core/src/render3/instructions/element.ts" 42 "AGENT: Consider the performance implications here."
```
### `submit_pr_review.sh`
Submits all locally staged inline comments as a single batched review via the GitHub Pull Request Reviews API.
**Usage:**
```bash
.agent/skills/pr_review/scripts/submit_pr_review.sh <PR_NUMBER> <EVENT_TYPE> [BODY]
```
**Options:**
- `EVENT_TYPE`: Must be `COMMENT`, `APPROVE`, or `REQUEST_CHANGES`. Never use `APPROVE` for external PRs.
- `BODY`: (Optional) A general summary comment for the review.
**Example:**
```bash
.agent/skills/pr_review/scripts/submit_pr_review.sh 12345 COMMENT "AGENT: I have left a few inline suggestions for your consideration."
```
@@ -0,0 +1,7 @@
# Router PR Review Guidelines
When reviewing pull requests that modify the Angular Router (`packages/router`), pay special attention to the following:
- **Timing Sensitivity**: The router is extremely sensitive to timing changes. Any changes that alter the asynchronous timing of navigations, resolvers, or guards are almost always breaking changes and must be scrutinized carefully.
- **Testing Practices**: Tests should usually use the `RouterTestingHarness`. Many existing tests are older and do not use this harness. Do not blindly follow the shape of existing tests when writing or reviewing new ones; encourage the use of modern testing utilities.
- **Feature Justification**: Changes to router core code should be well-justified. Consider whether the change is proven to be a core developer ask, such as resolving a highly upvoted GitHub issue or addressing a critical bug.
+32
View File
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
set -euo pipefail
# determine_review_type.sh <PR_NUMBER>
# Determines if the PR should be reviewed locally or remotely based on author.
if [ -z "$1" ]; then
echo "Usage: determine_review_type.sh <PR_NUMBER>"
exit 1
fi
PR_NUMBER=$1
# Get current authenticated user
CURRENT_USER=$(gh api user -q .login 2>/dev/null)
if [ $? -ne 0 ]; then
echo "Error: Could not determine current GitHub user. Are you logged in to gh?"
exit 1
fi
# Get PR author
PR_AUTHOR=$(gh pr view "$PR_NUMBER" --json author -q .author.login 2>/dev/null)
if [ $? -ne 0 ]; then
echo "Error: Could not retrieve PR information for $PR_NUMBER."
exit 1
fi
if [ "$CURRENT_USER" = "$PR_AUTHOR" ]; then
echo "local"
else
echo "remote"
fi
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -euo pipefail
# get_pr_comments.sh
# Fetches existing inline comments on a PR to avoid duplicate reviews.
# Usage: ./get_pr_comments.sh <PR_NUMBER>
if [ "$#" -lt 1 ]; then
echo "Usage: $0 <PR_NUMBER>"
exit 1
fi
PR_NUMBER="$1"
# Ensure gh cli is installed
if ! command -v gh &> /dev/null; then
echo "Error: gh CLI could not be found. Please install and authenticate."
exit 1
fi
# Get the current repository (e.g., angular/angular)
REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
# Fetch comments
gh api \
--paginate \
-H "Accept: application/vnd.github+json" \
"/repos/${REPO}/pulls/${PR_NUMBER}/comments" \
--jq '.[] | {id: .id, path: .path, line: .line, body: .body, user: .user.login}'
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -euo pipefail
# post_inline_comment.sh
# Adds an inline comment to a specific line in a PR via the GitHub API.
# Usage: ./post_inline_comment.sh <PR_NUMBER> <FILE_PATH> <LINE_NUMBER> <COMMENT_BODY>
if [ "$#" -lt 4 ]; then
echo "Usage: $0 <PR_NUMBER> <FILE_PATH> <LINE_NUMBER> <COMMENT_BODY>"
exit 1
fi
PR_NUMBER="$1"
FILE_PATH="$2"
LINE="$3"
BODY="$4"
# Ensure gh cli is installed
if ! command -v gh &> /dev/null; then
echo "Error: gh CLI could not be found. Please install and authenticate."
exit 1
fi
# Get the current repository (e.g., angular/angular)
REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
echo "Staging inline comment for PR #${PR_NUMBER} on ${FILE_PATH}:${LINE}..."
COMMENT_FILE="/tmp/angular_pr_${PR_NUMBER}_comments.json"
if [ ! -f "$COMMENT_FILE" ]; then
echo "[]" > "$COMMENT_FILE"
fi
# Append the new comment to the JSON array
jq --arg path "${FILE_PATH}" --argjson line "${LINE}" --arg body "${BODY}" \
'. += [{"path": $path, "line": $line, "body": $body}]' "$COMMENT_FILE" > "${COMMENT_FILE}.tmp" && mv "${COMMENT_FILE}.tmp" "$COMMENT_FILE"
echo "Comment successfully staged locally. Remember to call submit_pr_review.sh when finished to publish all comments as a single review!"
+31
View File
@@ -0,0 +1,31 @@
#!/usr/bin/env bash
set -euo pipefail
# reply_pr_comment.sh <PR_NUMBER> <COMMENT_ID> <REPLY_BODY>
# Replies to an existing PR comment thread. Note: COMMENT_ID must be the ID of the top-level comment in the thread you are replying to.
if [ "$#" -lt 3 ]; then
echo "Usage: reply_pr_comment.sh <PR_NUMBER> <COMMENT_ID> <REPLY_BODY>"
exit 1
fi
PR_NUMBER="$1"
COMMENT_ID="$2"
BODY="$3"
# Ensure gh cli is installed
if ! command -v gh &> /dev/null; then
echo "Error: gh CLI could not be found. Please install and authenticate."
exit 1
fi
# Get the current repository (e.g., angular/angular)
REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
# Reply to the thread using the provided comment ID
gh api \
--silent \
--method POST \
-H "Accept: application/vnd.github+json" \
"/repos/${REPO}/pulls/${PR_NUMBER}/comments/${COMMENT_ID}/replies" \
-f body="$BODY"
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env bash
set -euo pipefail
# submit_pr_review.sh
# Submits a batched PR review using comments previously staged by post_inline_comment.sh
# Usage: ./submit_pr_review.sh <PR_NUMBER> <EVENT_TYPE> [BODY]
# EVENT_TYPE must be COMMENT, APPROVE, or REQUEST_CHANGES
if [ "$#" -lt 2 ]; then
echo "Usage: $0 <PR_NUMBER> <EVENT_TYPE> [BODY]"
echo "EVENT_TYPE must be COMMENT, APPROVE, or REQUEST_CHANGES"
exit 1
fi
PR_NUMBER="$1"
EVENT="$2"
BODY="${3:-}"
COMMENT_FILE="/tmp/angular_pr_${PR_NUMBER}_comments.json"
if ! command -v gh &> /dev/null; then
echo "Error: gh CLI could not be found. Please install and authenticate."
exit 1
fi
REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
# Check if there are staged comments
COMMENTS="[]"
if [ -f "$COMMENT_FILE" ]; then
COMMENTS=$(cat "$COMMENT_FILE")
fi
echo "Submitting review for PR #${PR_NUMBER}..."
# Create the payload
PAYLOAD_FILE="/tmp/angular_pr_${PR_NUMBER}_payload.json"
jq -n --arg event "$EVENT" --arg body "$BODY" --argjson comments "$COMMENTS" \
'{event: $event, body: $body, comments: $comments}' > "$PAYLOAD_FILE"
# Post the review using the GitHub Pull Request Reviews API
gh api \
--method POST \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"/repos/${REPO}/pulls/${PR_NUMBER}/reviews" \
--input "$PAYLOAD_FILE"
echo "Review submitted successfully!"
rm -f "$COMMENT_FILE"
rm -f "$PAYLOAD_FILE"
+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.
+2 -1
View File
@@ -7,4 +7,5 @@ code_review:
help: false
summary: false
code_review: false
ignore_patterns: []
ignore_patterns:
- pnpm-lock.yaml
+131 -31
View File
@@ -10481,7 +10481,7 @@ var JSONStringify = (value, replacer, space) => {
const convertedToCustomJSON = originalStringify(
value,
(key, value2) => {
const isNoise = typeof value2 === "string" && Boolean(value2.match(noiseValue));
const isNoise = typeof value2 === "string" && noiseValue.test(value2);
if (isNoise)
return value2.toString() + "n";
if (typeof value2 === "bigint")
@@ -10501,12 +10501,29 @@ var JSONStringify = (value, replacer, space) => {
const denoisedJSON = processedJSON.replace(noiseStringify, "$1$2$3");
return denoisedJSON;
};
var isContextSourceSupported = () => JSON.parse("1", (_, __, context3) => !!context3 && context3.source === "1");
var featureCache = /* @__PURE__ */ new Map();
var isContextSourceSupported = () => {
const parseFingerprint = JSON.parse.toString();
if (featureCache.has(parseFingerprint)) {
return featureCache.get(parseFingerprint);
}
try {
const result = JSON.parse(
"1",
(_, __, context3) => !!context3?.source && context3.source === "1"
);
featureCache.set(parseFingerprint, result);
return result;
} catch {
featureCache.set(parseFingerprint, false);
return false;
}
};
var convertMarkedBigIntsReviver = (key, value, context3, userReviver) => {
const isCustomFormatBigInt = typeof value === "string" && value.match(customFormat);
const isCustomFormatBigInt = typeof value === "string" && customFormat.test(value);
if (isCustomFormatBigInt)
return BigInt(value.slice(0, -1));
const isNoiseValue = typeof value === "string" && value.match(noiseValue);
const isNoiseValue = typeof value === "string" && noiseValue.test(value);
if (isNoiseValue)
return value.slice(0, -1);
if (typeof userReviver !== "function")
@@ -10538,7 +10555,7 @@ var JSONParse = (text, reviver) => {
stringsOrLargeNumbers,
(text2, digits, fractional, exponential) => {
const isString = text2[0] === '"';
const isNoise = isString && Boolean(text2.match(noiseValueWithQuotes));
const isNoise = isString && noiseValueWithQuotes.test(text2);
if (isNoise)
return text2.substring(0, text2.length - 1) + 'n"';
const isFractionalOrExponential = fractional || exponential;
@@ -22961,6 +22978,7 @@ var require_stringify = __commonJS2({
nullStr: "null",
simpleKeys: false,
singleQuote: null,
trailingComma: false,
trueStr: "true",
verifyAliasOrder: true
}, doc.schema.toStringOptions, options);
@@ -23466,12 +23484,19 @@ ${indent}${line}` : "\n";
if (comment)
reqNewline = true;
let str = stringify.stringify(item, itemCtx, () => comment = null);
if (i < items.length - 1)
reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n"));
if (i < items.length - 1) {
str += ",";
} else if (ctx.options.trailingComma) {
if (ctx.options.lineWidth > 0) {
reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth);
}
if (reqNewline) {
str += ",";
}
}
if (comment)
str += stringifyComment.lineComment(str, itemIndent, commentString(comment));
if (!reqNewline && (lines.length > linesAtValue || str.includes("\n")))
reqNewline = true;
lines.push(str);
linesAtValue = lines.length;
}
@@ -26401,17 +26426,22 @@ var require_compose_node = __commonJS2({
case "block-map":
case "block-seq":
case "flow-collection":
node = composeCollection.composeCollection(CN, ctx, token, props, onError);
if (anchor)
node.anchor = anchor.source.substring(1);
try {
node = composeCollection.composeCollection(CN, ctx, token, props, onError);
if (anchor)
node.anchor = anchor.source.substring(1);
} catch (error2) {
const message = error2 instanceof Error ? error2.message : String(error2);
onError(token, "RESOURCE_EXHAUSTION", message);
}
break;
default: {
const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`;
onError(token, "UNEXPECTED_TOKEN", message);
node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError);
isSrcToken = false;
}
}
node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError));
if (anchor && node.anchor === "")
onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string");
if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) {
@@ -29176,7 +29206,7 @@ var JSONStringify2 = (value, replacer, space) => {
const convertedToCustomJSON = originalStringify2(
value,
(key, value2) => {
const isNoise = typeof value2 === "string" && Boolean(value2.match(noiseValue2));
const isNoise = typeof value2 === "string" && noiseValue2.test(value2);
if (isNoise)
return value2.toString() + "n";
if (typeof value2 === "bigint")
@@ -29196,12 +29226,29 @@ var JSONStringify2 = (value, replacer, space) => {
const denoisedJSON = processedJSON.replace(noiseStringify2, "$1$2$3");
return denoisedJSON;
};
var isContextSourceSupported2 = () => JSON.parse("1", (_, __, context3) => !!context3 && context3.source === "1");
var featureCache2 = /* @__PURE__ */ new Map();
var isContextSourceSupported2 = () => {
const parseFingerprint = JSON.parse.toString();
if (featureCache2.has(parseFingerprint)) {
return featureCache2.get(parseFingerprint);
}
try {
const result = JSON.parse(
"1",
(_, __, context3) => !!context3?.source && context3.source === "1"
);
featureCache2.set(parseFingerprint, result);
return result;
} catch {
featureCache2.set(parseFingerprint, false);
return false;
}
};
var convertMarkedBigIntsReviver2 = (key, value, context3, userReviver) => {
const isCustomFormatBigInt = typeof value === "string" && value.match(customFormat2);
const isCustomFormatBigInt = typeof value === "string" && customFormat2.test(value);
if (isCustomFormatBigInt)
return BigInt(value.slice(0, -1));
const isNoiseValue = typeof value === "string" && value.match(noiseValue2);
const isNoiseValue = typeof value === "string" && noiseValue2.test(value);
if (isNoiseValue)
return value.slice(0, -1);
if (typeof userReviver !== "function")
@@ -29233,7 +29280,7 @@ var JSONParse2 = (text, reviver) => {
stringsOrLargeNumbers2,
(text2, digits, fractional, exponential) => {
const isString = text2[0] === '"';
const isNoise = isString && Boolean(text2.match(noiseValueWithQuotes2));
const isNoise = isString && noiseValueWithQuotes2.test(text2);
if (isNoise)
return text2.substring(0, text2.length - 1) + 'n"';
const isFractionalOrExponential = fractional || exponential;
@@ -32535,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;
@@ -32547,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 {
@@ -32570,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;
}
@@ -33536,7 +33636,7 @@ tmp/lib/tmp.js:
*)
*)
@angular/ng-dev/bundles/chunk-34WCNHIG.mjs:
@angular/ng-dev/bundles/chunk-TUTTLTAK.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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Saucelabs Variables
uses: angular/dev-infra/github-actions/saucelabs@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
- 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}}'
+1 -1
View File
@@ -40,7 +40,7 @@ jobs:
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}}
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
- 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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
- uses: angular/dev-infra/github-actions/branch-manager@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+2 -2
View File
@@ -34,11 +34,11 @@ 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}}
- run: pnpm install --frozen-lockfile
- uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
- uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
bazelrc: ./.bazelrc.user
+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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@bc22e01685c56e89e7813fd8e26f33dc47f87e15 # v7.1.5
uses: cypress-io/github-action@783cb3f07983868532cabaedaa1e6c00ff4786a8 # v7.1.9
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Build adev
+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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
- 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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
- 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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
- 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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
- 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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Saucelabs Variables
uses: angular/dev-infra/github-actions/saucelabs@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
- 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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@bc22e01685c56e89e7813fd8e26f33dc47f87e15 # v7.1.5
uses: cypress-io/github-action@783cb3f07983868532cabaedaa1e6c00ff4786a8 # v7.1.9
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
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@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
with:
sarif_file: results.sarif
+1 -1
View File
@@ -1 +1 @@
22.22.1
22.22.2
+2 -2
View File
@@ -434,7 +434,7 @@ groups:
])
reviewers:
users:
- marktechson
- MarkTechson
- kirjs
- ~JeanMeche
- ~dgp1130
@@ -487,7 +487,7 @@ groups:
<<: *defaults
conditions:
- >
contains_any_globs(files, [
contains_any_globs(files.exclude('packages/core/primitives/**/*spec.ts'), [
'packages/core/primitives/**/{*,.*}',
])
reviewers:
+134
View File
@@ -1,3 +1,137 @@
<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
| Commit | Type | Description |
| -- | -- | -- |
| [fea25d1a60](https://github.com/angular/angular/commit/fea25d1a60ecaba1599d9cd9b8df27109ed195c5) | fix | register SVG animation attributes in URL security context ([#67797](https://github.com/angular/angular/pull/67797)) |
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [bba5ed8e64](https://github.com/angular/angular/commit/bba5ed8e643b9c3f680e7e539c3d744ad6905e59) | fix | prevent recursive scope checks for invalid NgModule imports |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [d04ddd73df](https://github.com/angular/angular/commit/d04ddd73dfc03f420afbdde964c5119f338af135) | fix | prevent binding unsafe attributes on SVG animation elements ([#67797](https://github.com/angular/angular/pull/67797)) |
| [8fd896e99a](https://github.com/angular/angular/commit/8fd896e99a13855c6569f29efe7e578c301e13ee) | fix | resolve component import by exact specifier in route lazy-loading schematic |
| [b682c62873](https://github.com/angular/angular/commit/b682c628731b86a4884e50abb2f5fa73ac0ad057) | fix | treat `object[data]` as resource URL context ([#67797](https://github.com/angular/angular/pull/67797)) |
### localize
| Commit | Type | Description |
| -- | -- | -- |
| [3c41e74fdd](https://github.com/angular/angular/commit/3c41e74fdd279f683156b654699a9312a850add0) | fix | validate locale in getOutputPathFn to prevent path traversal |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [0960592d3d](https://github.com/angular/angular/commit/0960592d3d4fad110d5598144fda9f2488520826) | fix | pass outlet context to split to fix empty path named outlets |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.6"></a>
# 21.2.6 (2026-03-25)
### common
| Commit | Type | Description |
| -- | -- | -- |
| [b4ab6ba2e8](https://github.com/angular/angular/commit/b4ab6ba2e84a18309b0bb5dd68311ff1776b1cb4) | fix | avoid redundant image fetch on destroy with auto sizes |
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [880a57d4b3](https://github.com/angular/angular/commit/880a57d4b34af5aa27cd5bee11fa218ade6444bb) | fix | prevent shimCssText from adding extra blank lines per CSS comment |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [ad0156e056](https://github.com/angular/angular/commit/ad0156e056e60ffebfeb804fda70dce88d9475a8) | fix | fixes a regression with animate.leave and reordering |
### migrations
| Commit | Type | Description |
| -- | -- | -- |
| [73d6b01b47](https://github.com/angular/angular/commit/73d6b01b47bb6762d182f1cd891f8ad4d7f688e1) | fix | inject migration not work in multi-project workspace with option path |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.5"></a>
# 21.2.5 (2026-03-18)
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [334ae10168](https://github.com/angular/angular/commit/334ae10168fdad15cd1390180e2994b4eb65349b) | fix | ensure generated code compiles |
| [23ea431c4e](https://github.com/angular/angular/commit/23ea431c4ec45cbb4a7db9839969e7cb23b07f58) | fix | parse named HTML entities containing digits |
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [26c43d14ba](https://github.com/angular/angular/commit/26c43d14baad1a6b3629a77825e702a97a4f8482) | fix | escape template literal in TCB |
| [67e0ba7e03](https://github.com/angular/angular/commit/67e0ba7e03bb940639f0eafb3af45015e9727eac) | fix | generic types not filled out correctly in type check block |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [1890c3008b](https://github.com/angular/angular/commit/1890c3008bbb41b7143b7ede09bed1f7704744fb) | fix | clean up dehydrated views during HMR component replacement |
| [bf948be4c2](https://github.com/angular/angular/commit/bf948be4c2c88c604e428cba35e3b9e532bfe5b0) | fix | run linked signal equality check without reactive consumer |
### migrations
| Commit | Type | Description |
| -- | -- | -- |
| [076d41c3f6](https://github.com/angular/angular/commit/076d41c3f6496eb6c6f84b54e2d2ca85c1b35e64) | fix | prevent trailing comma syntax errors after removing NgStyle |
### service-worker
| Commit | Type | Description |
| -- | -- | -- |
| [e19150d2b5](https://github.com/angular/angular/commit/e19150d2b596e87c69bee61f478c3e9c7cbc8f67) | fix | preserve redirect policy on reconstructed asset requests |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.4"></a>
# 21.2.4 (2026-03-12)
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [ed2d324f9c](https://github.com/angular/angular/commit/ed2d324f9cc12aab6cfa0569ef10b73243a62c65) | fix | disallow translations of iframe src |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [abbd8797bb](https://github.com/angular/angular/commit/abbd8797bbd3ae53a10033c39bd895b5b85a4fae) | fix | reverts "feat(core): add support for nested animations" |
| [d1dcd16c5b](https://github.com/angular/angular/commit/d1dcd16c5b40291aa3fa2dc84d22842cd657b201) | fix | sanitize translated form attributes |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.3"></a>
# 21.2.3 (2026-03-11)
### core
+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:
+25 -27
View File
@@ -6,40 +6,40 @@ 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.5")
bazel_dep(name = "aspect_rules_js", version = "2.9.2")
bazel_dep(name = "aspect_rules_esbuild", version = "0.25.0")
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 = "aspect_bazel_lib", version = "2.22.5")
bazel_dep(name = "tar.bzl", version = "0.9.0")
bazel_dep(name = "bazel_lib", version = "3.2.2")
bazel_dep(name = "tar.bzl", version = "0.10.1")
bazel_dep(name = "yq.bzl", version = "0.3.5")
bazel_dep(name = "rules_angular")
git_override(
module_name = "rules_angular",
commit = "d746c4f75e42cffe389d1ab077f4639be2bc78d1",
commit = "46fdabe25ad865093d1be752c778ca4d81f222f5",
remote = "https://github.com/angular/rules_angular.git",
)
bazel_dep(name = "devinfra")
git_override(
module_name = "devinfra",
commit = "e4dcda13e469f50a6bd92667e2ded8b98173bf2c",
commit = "80659f606f547e99bd08aa15c98479371b3bacab",
remote = "https://github.com/angular/dev-infra.git",
)
bazel_dep(name = "rules_sass")
git_override(
module_name = "rules_sass",
commit = "1184a80751a21af8348f308abc5b38a41f26850e",
commit = "fa129d1ee0652fe0c63ef82d13dc20d4a4e5f919",
remote = "https://github.com/angular/rules_sass.git",
)
bazel_dep(name = "rules_browsers")
git_override(
module_name = "rules_browsers",
commit = "8231142fc5516323b040883bf774ba8362645387",
commit = "dc3e432662fe9f99eb7f030a35a22d4ff77d54c7",
remote = "https://github.com/angular/rules_browsers.git",
)
@@ -49,15 +49,15 @@ use_repo(yq, "yq_toolchains")
node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node")
node.toolchain(
node_repositories = {
"22.22.1-darwin_arm64": ("node-v22.22.1-darwin-arm64.tar.gz", "node-v22.22.1-darwin-arm64", "679ad4966339e4ef4900f57996714864e4211b898825bb840c3086c419fbcef2"),
"22.22.1-darwin_amd64": ("node-v22.22.1-darwin-x64.tar.gz", "node-v22.22.1-darwin-x64", "07b13722d558790fca20bb1ecf61bde24b7a4863111f7be77fc57251a407359a"),
"22.22.1-linux_arm64": ("node-v22.22.1-linux-arm64.tar.xz", "node-v22.22.1-linux-arm64", "0f3550d58d45e5d3cf7103d9e3f69937f09fe82fb5dd474c66a5d816fa58c9ee"),
"22.22.1-linux_ppc64le": ("node-v22.22.1-linux-ppc64le.tar.xz", "node-v22.22.1-linux-ppc64le", "a65a44cf0224505f052b90357b763dbc1ea9148f4f5f2284f0596cf2000f819b"),
"22.22.1-linux_s390x": ("node-v22.22.1-linux-s390x.tar.xz", "node-v22.22.1-linux-s390x", "e08b3a73d0bd840e008f589e4be4a2ef3d4a0c59015f4f20a04ed7fc968042a2"),
"22.22.1-linux_amd64": ("node-v22.22.1-linux-x64.tar.xz", "node-v22.22.1-linux-x64", "9a6bc82f9b491279147219f6a18add1e18424dce90d41d2a5fcd69d4924ba3aa"),
"22.22.1-windows_amd64": ("node-v22.22.1-win-x64.zip", "node-v22.22.1-win-x64", "877cb93829e14fffbbc7903e7d8037336c9a79f3ea43c5d0b8c2379b79da56de"),
"22.22.2-darwin_arm64": ("node-v22.22.2-darwin-arm64.tar.gz", "node-v22.22.2-darwin-arm64", "db4b275b83736df67533529a18cc55de2549a8329ace6c7bcc68f8d22d3c9000"),
"22.22.2-darwin_amd64": ("node-v22.22.2-darwin-x64.tar.gz", "node-v22.22.2-darwin-x64", "12a6abb9c2902cf48a21120da13f87fde1ed1b71a13330712949e8db818708ba"),
"22.22.2-linux_arm64": ("node-v22.22.2-linux-arm64.tar.xz", "node-v22.22.2-linux-arm64", "e9e1930fd321a470e29bb68f30318bf58e3ecb4acb4f1533fb19c58328a091fe"),
"22.22.2-linux_ppc64le": ("node-v22.22.2-linux-ppc64le.tar.xz", "node-v22.22.2-linux-ppc64le", "14045b5a5030d35ca0030fb7e870bd11a651eb9b57323ebc0021e8d78ac6bac9"),
"22.22.2-linux_s390x": ("node-v22.22.2-linux-s390x.tar.xz", "node-v22.22.2-linux-s390x", "9e4a07c291b8949289c6ea8ee61b1d14666a4810feae776a8d1eb1f57e03a2fb"),
"22.22.2-linux_amd64": ("node-v22.22.2-linux-x64.tar.xz", "node-v22.22.2-linux-x64", "88fd1ce767091fd8d4a99fdb2356e98c819f93f3b1f8663853a2dee9b438068a"),
"22.22.2-windows_amd64": ("node-v22.22.2-win-x64.zip", "node-v22.22.2-win-x64", "7c93e9d92bf68c07182b471aa187e35ee6cd08ef0f24ab060dfff605fcc1c57c"),
},
node_version = "22.22.1",
node_version = "22.22.2",
)
use_repo(node, "nodejs_toolchains")
use_repo(node, "nodejs_darwin_amd64")
@@ -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.32.0",
pnpm_version_integrity = "sha512-myY0uz/tVgHDNjPy2SWT9QYnCjljuMUdKy1qgo2mFc5One6++WFMzrvBOsjTwPnJzM61g8achXhDb6R327INcA==",
pnpm_version = "10.33.0",
pnpm_version_integrity = "sha512-EFaLtKavtYyes2MNqQzJUWQXq+vT+rvmc58K55VyjaFJHp21pUTHatjrdXD1xLs9bGN7LLQb/c20f6gjyGSTGQ==",
)
use_repo(pnpm, "pnpm")
@@ -117,7 +117,7 @@ use_repo(npm, "npm")
rules_ts_ext = use_extension("@aspect_rules_ts//ts:extensions.bzl", "ext")
rules_ts_ext.deps(
name = "angular_npm_typescript",
# Obtained by: curl --silent https://registry.npmjs.org/typescript/5.9.3 | jq -r '.dist.integrity'
# Obtained by: curl --silent https://registry.npmjs.org/typescript/6.0.1-rc | jq -r '.dist.integrity'
ts_integrity = "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
ts_version = "5.9.3",
)
@@ -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",
+311 -458
View File
File diff suppressed because it is too large Load Diff
+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.0",
"@angular/build": "21.2.0",
"@angular/cdk": "21.2.0",
"@angular/cli": "21.2.0",
"@angular/aria": "21.2.6",
"@angular/build": "21.2.7",
"@angular/cdk": "21.2.6",
"@angular/cli": "21.2.7",
"@angular/common": "workspace:*",
"@angular/compiler": "workspace:*",
"@angular/compiler-cli": "workspace:*",
"@angular/core": "workspace:*",
"@angular/docs": "workspace:*",
"@angular/forms": "workspace:*",
"@angular/material": "21.2.0",
"@angular/material": "21.2.6",
"@angular/platform-browser": "workspace:*",
"@angular/platform-server": "workspace:*",
"@angular/router": "workspace:*",
"@angular/ssr": "21.2.0",
"@angular/ssr": "21.2.7",
"@codemirror/autocomplete": "6.20.0",
"@codemirror/commands": "6.10.1",
"@codemirror/lang-angular": "0.1.4",
@@ -53,7 +53,7 @@
matrixParams: 'ignored',
fragment: 'ignored',
}"
(click)="emitClickOnLink()"
(click)="emitClickOnLink(item)"
[matTooltip]="item.label"
[matTooltipDisabled]="itemLabel.length < 27"
matTooltipPosition="after"
@@ -153,5 +153,6 @@ describe('NavigationList', () => {
class FakeNavigationListState {
isOpened = signal(true);
activeNavigationItem = signal(navigationItems.at(1));
crossCategoryOrigin = signal<NavigationItem | undefined>(undefined);
toggleItem(item: NavigationItem) {}
}
@@ -40,6 +40,7 @@ export class NavigationList {
readonly linkClicked = output<void>();
private readonly navigationState = inject(NavigationState);
private readonly crossCategoryOrigin = this.navigationState.crossCategoryOrigin;
readonly activeItem = this.navigationState.activeNavigationItem;
@@ -51,10 +52,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;
+2 -2
View File
@@ -1,6 +1,6 @@
load("@aspect_bazel_lib//lib:copy_to_bin.bzl", _copy_to_bin = "copy_to_bin")
load("@aspect_bazel_lib//lib:copy_to_directory.bzl", _copy_to_directory = "copy_to_directory")
load("@aspect_rules_esbuild//esbuild:defs.bzl", _esbuild = "esbuild")
load("@bazel_lib//lib:copy_to_bin.bzl", _copy_to_bin = "copy_to_bin")
load("@bazel_lib//lib:copy_to_directory.bzl", _copy_to_directory = "copy_to_directory")
load("@devinfra//bazel/private:path_relative_to_label.bzl", _path_relative_to_label = "path_relative_to_label")
load(
"//tools:defaults.bzl",
@@ -43,15 +43,6 @@ describe('ClickOutside', () => {
expect(clickedOutsideSpy).not.toHaveBeenCalled();
});
it('should not docsClickOutside be emitted when user click inside `content` element', () => {
const clickedOutsideSpy = spyOn(component, 'clickedOutside');
const button = fixture.debugElement.query(By.css('button[id="ignoreThisButton"]'));
button.nativeElement.click();
expect(clickedOutsideSpy).not.toHaveBeenCalled();
});
});
@Component({
@@ -17,5 +17,6 @@ export interface NavigationItem {
contentPath?: string;
status?: 'new' | 'updated';
category?: string;
isCrossReferenced?: boolean;
preserveOtherCategoryOrder?: boolean; // true by default
}
@@ -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 ? (
@@ -27,14 +27,14 @@ export function CliCommandReference(entry: CliCommandRenderable) {
<div className={'shiki line cli'}>
ng {commandName(entry, command)}
{entry.argumentsLabel && (
<button member-id={'Arguments'} className="shiki-ln-line-argument">
<span member-id={'Arguments'} className="shiki-ln-line-argument">
{entry.argumentsLabel}
</button>
</span>
)}
{entry.optionsLabel && (
<button member-id={'Options'} className="shiki-ln-line-option">
<span member-id={'Options'} className="shiki-ln-line-option">
{entry.optionsLabel}
</button>
</span>
)}
</div>
</code>
@@ -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}`);
}
});
});
@@ -95,6 +95,7 @@ export async function addRenderableCodeToc<T extends DocEntry & HasModuleName>(
{
language: 'typescript',
apiEntries: getSymbolsAsApiEntries(),
removeWhitespace: false,
},
);
@@ -11,7 +11,7 @@ import {chromium} from 'playwright-core';
import {Mermaid, MermaidConfig} from 'mermaid';
import {resolve, join} from 'path';
const runfilesDir = process.env['RUNFILES']!;
const runfilesDir = process.env['JS_BINARY__RUNFILES']!;
// Declare mermarid in the context of this file so that typescript doesn't get upset when we
// access it within the `page.evaluate` function. At runtime the context in with the method
// is run difference than this file, but this makes typescript happy.
@@ -135,4 +135,29 @@ describe('markdown to html', () => {
// We ensure that we still style the heading content
expect(markdownDocument.querySelector('strong')).toBeDefined();
});
it('should strip all HTML tags from the aria-label attribute', () => {
const markdownDocument = JSDOM.fragment(
parseMarkdown(
'## **Phase 5: Experimental Signal Forms (⚠️ WARNING: Subject to Change)**',
rendererContext,
),
);
const h2Anchor = markdownDocument.querySelector('h2 > a');
expect(h2Anchor!.innerHTML).toBe(
'<strong><span class="docs-emoji">Phase 5: Experimental Signal Forms (⚠️ WARNING: Subject to Change)</span></strong>',
);
expect(h2Anchor!.getAttribute('aria-label')).toBe(
'Link to Phase 5: Experimental Signal Forms (⚠️ WARNING: Subject to Change)',
);
});
it('should escape double quotes from the aria-label attribute', () => {
const markdownDocument = JSDOM.fragment(parseMarkdown('## "Special" heading', rendererContext));
const h2Anchor = markdownDocument.querySelector('h2 > a');
expect(h2Anchor!.innerHTML).toBe('"Special" heading');
expect(h2Anchor!.getAttribute('aria-label')).toBe('Link to "Special" heading');
});
});
@@ -31,7 +31,7 @@ export function headingRender(
// Replace code backticks and remove custom ID syntax from the displayed label
let label = parsedText.replace(/`(.*?)`/g, '<code>$1</code>');
label = label.replace(/{#\s*[\w-]+\s*}/g, '').trim();
const normalizedLabel = label.replace(/<\/?code>/g, '');
const normalizedLabel = label.replace(/<[^>]+>/g, '').replace(/"/g, '&quot;');
return `
<h${depth} id="${link}">
+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
View File
@@ -426,6 +426,14 @@
.docs-ref-content {
padding: 1rem 0;
.docs-ref-option-and-description {
.docs-reference-option {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
}
&:not(:first-child) {
border-block-start: 1px solid var(--senary-contrast);
}
+18 -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;
@@ -252,7 +268,7 @@
.docs-nav-card-svg {
width: 350px;
@container header (max-width: 550px) {
@container header (max-width: $compact-breakpoint) {
display: none;
}
}
+9 -1
View File
@@ -36,6 +36,15 @@ $code-font-size: 0.875rem;
}
}
.shiki.line.cli {
span {
color: var(--quaternary-contrast);
font-family: var(--inter-font);
font-weight: 600;
font-size: 13px;
}
}
.docs-light-mode .shiki {
color: var(--shiki-light);
background-color: var(--shiki-light-bg);
@@ -46,7 +55,6 @@ $code-font-size: 0.875rem;
/* Optional, if you also want font styles */
font-style: var(--shiki-light-font-style);
font-weight: var(--shiki-light-font-weight);
text-decoration: var(--shiki-light-text-decoration);
}
.shiki-ln-line-highlighted,
+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);
}
@@ -41,3 +41,6 @@ export const setCookieConsent = (state: 'denied' | 'granted'): void => {
}
}
};
// This fixes the RollupError: Exported variable "global" is not defined.
export {};
+6
View File
@@ -15,6 +15,7 @@ import {
provideZonelessChangeDetection,
provideEnvironmentInitializer,
} from '@angular/core';
import {UrlSerializer} from '@angular/router';
import {
DOCS_CONTENT_LOADER,
ENVIRONMENT,
@@ -33,6 +34,7 @@ import {CustomErrorHandler} from './core/services/errors-handling/error-handler'
import {ExampleContentLoader} from './core/services/example-content-loader.service';
import {routerProviders} from './routing/router_providers';
import {TYPESCRIPT_VFS_WORKER_PROVIDER} from './editor/code-editor/workers/factory-provider';
import {AdevUrlSerializer} from './core/services/routing/adev-url-serializer';
export const appConfig: ApplicationConfig = {
providers: [
@@ -53,5 +55,9 @@ export const appConfig: ApplicationConfig = {
deps: [DOCUMENT],
},
TYPESCRIPT_VFS_WORKER_PROVIDER,
{
provide: UrlSerializer,
useClass: AdevUrlSerializer,
},
],
};
@@ -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;
}
@@ -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;
}
@@ -0,0 +1,25 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {DefaultUrlSerializer, UrlTree} from '@angular/router';
/**
* Custom URL serializer extending the default behavior
* with Adev-specific behavior.
*/
export class AdevUrlSerializer extends DefaultUrlSerializer {
override parse(url: string): UrlTree {
// Since the app host/server is decoding encoded forward slashes,
// we perform this on the client as well in order to maintain
// a consistent behavior between the two environments and
// avoid opening a different page on client hydration (presumably, 404).
url = url.replaceAll(/%2(F|f)/g, '/');
return super.parse(url);
}
}
@@ -0,0 +1,36 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {TestBed} from '@angular/core/testing';
import {UrlSerializer} from '@angular/router';
import {AdevUrlSerializer} from './adev-url-serializer';
describe('AdevUrlSerializer', () => {
let serializer: UrlSerializer;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{
provide: UrlSerializer,
useClass: AdevUrlSerializer,
},
],
});
serializer = TestBed.inject(UrlSerializer);
});
it('should decode encoded forward slash (%2F)', () => {
// Uppercase hex
expect(serializer.parse('page%2Fabout').toString()).toBe('/page/about');
// Lowercase hex
expect(serializer.parse('page%2fabout').toString()).toBe('/page/about');
});
});
@@ -19,6 +19,8 @@ import {NodeRuntimeSandbox} from '../node-runtime-sandbox.service';
import {TypingsLoader} from '../typings-loader.service';
import {FileAndContentRecord} from '@angular/docs';
import {DomSanitizer} from '@angular/platform-browser';
import {NodeRuntimeState} from '../node-runtime-state.service';
import {CODE_EDITOR_EXTENSIONS} from './constants/code-editor-extensions';
import {LANGUAGES} from './constants/code-editor-languages';
import {getAutocompleteExtension} from './extensions/autocomplete';
@@ -26,10 +28,9 @@ import {getDiagnosticsExtension} from './extensions/diagnostics';
import {getTooltipExtension} from './extensions/tooltip';
import {DiagnosticsState} from './services/diagnostics-state.service';
import {TsVfsWorkerActions} from './workers/enums/actions';
import {TYPESCRIPT_VFS_WORKER_FACTORY} from './workers/factory-provider';
import {CodeChangeRequest} from './workers/interfaces/code-change-request';
import {ActionMessage} from './workers/interfaces/message';
import {NodeRuntimeState} from '../node-runtime-state.service';
import {TYPESCRIPT_VFS_WORKER_FACTORY} from './workers/factory-provider';
export interface EditorFile {
filename: string;
@@ -81,6 +82,7 @@ export class CodeMirrorEditor {
private readonly typingsLoader = inject(TypingsLoader);
private readonly destroyRef = inject(DestroyRef);
private readonly diagnosticsState = inject(DiagnosticsState);
private readonly domSanitizer = inject(DomSanitizer);
private readonly tsVfsWorkerFactory = inject(TYPESCRIPT_VFS_WORKER_FACTORY);
private tsVfsWorker: Worker | null = null;
@@ -448,7 +450,12 @@ export class CodeMirrorEditor {
this.sendRequestToTsVfs,
this.diagnosticsState,
),
getTooltipExtension(this.eventManager$, this.currentFile, this.sendRequestToTsVfs),
getTooltipExtension(
this.eventManager$,
this.currentFile,
this.sendRequestToTsVfs,
this.domSanitizer,
),
];
}
@@ -0,0 +1,41 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {DomSanitizer} from '@angular/platform-browser';
import {TestBed} from '@angular/core/testing';
import {getMarkedHtmlFromString, getTagsHtml} from './tooltip';
describe('getMarkedHtmlFromString', () => {
it('sanitizes markdown HTML content before assigning to innerHTML', () => {
const markdownContent = 'hello <img src=x onerror="alert(1)" />';
const domSanitizer = TestBed.inject(DomSanitizer);
const result = getMarkedHtmlFromString(markdownContent, domSanitizer);
expect(result.innerHTML.trim()).toBe('<p>hello <img src="x"></p>');
expect(result.innerHTML).not.toContain('onerror');
});
});
describe('getTagsHtml', () => {
it('sanitizes JSDoc tag content before assigning to innerHTML', () => {
const tags = [
{
name: 'example',
text: [{text: 'hello <img src=x onerror="alert(1)" />'}],
},
] as any[];
const domSanitizer = TestBed.inject(DomSanitizer);
const result = getTagsHtml(tags, domSanitizer);
expect(result.innerHTML).toContain('@example');
expect(result.innerHTML).not.toContain('onerror');
});
});
@@ -6,7 +6,9 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Signal} from '@angular/core';
import {SecurityContext, Signal} from '@angular/core';
import {DomSanitizer} from '@angular/platform-browser';
import {Tooltip, hoverTooltip} from '@codemirror/view';
import {marked} from 'marked';
import {Subject, filter, take} from 'rxjs';
@@ -23,6 +25,7 @@ export const getTooltipExtension = (
emitter: Subject<ActionMessage<DisplayTooltipResponse>>,
currentFile: Signal<EditorFile>,
sendRequestToTsVfs: (request: ActionMessage<DisplayTooltipRequest>) => void,
domSanitizer: DomSanitizer,
) => {
return hoverTooltip(
async (_, pos: number): Promise<Tooltip | null> => {
@@ -58,9 +61,9 @@ export const getTooltipExtension = (
// use documentation if available as it's more informative than tags
if (documentation?.[0]?.text) {
tooltip.appendChild(getMarkedHtmlFromString(documentation[0]?.text));
tooltip.appendChild(getMarkedHtmlFromString(documentation[0]?.text, domSanitizer));
} else if (tags?.length) {
tooltip.appendChild(getTagsHtml(tags));
tooltip.appendChild(getTagsHtml(tags, domSanitizer));
}
return {
@@ -91,9 +94,13 @@ function forceTooltipScrollTop() {
}
}
function getMarkedHtmlFromString(content: string): HTMLDivElement {
export function getMarkedHtmlFromString(
content: string,
domSanitizer: DomSanitizer,
): HTMLDivElement {
const wrapper = document.createElement('div');
wrapper.innerHTML = marked(content) as string;
const sanitizedHtml = renderAndSanitizeMarkdownToHtml(content, domSanitizer);
wrapper.innerHTML = sanitizedHtml;
return wrapper;
}
@@ -123,7 +130,7 @@ function getHtmlFromDisplayParts(displayParts: ts.SymbolDisplayPart[]): HTMLDivE
return wrapper;
}
function getTagsHtml(tags: ts.JSDocTagInfo[]): HTMLDivElement {
export function getTagsHtml(tags: ts.JSDocTagInfo[], domSanitizer: DomSanitizer): HTMLDivElement {
const tagsWrapper = document.createElement('div');
let contentString = '';
@@ -138,7 +145,14 @@ function getTagsHtml(tags: ts.JSDocTagInfo[]): HTMLDivElement {
}
}
tagsWrapper.innerHTML = marked(contentString) as string;
const sanitizedHtml = renderAndSanitizeMarkdownToHtml(contentString, domSanitizer);
tagsWrapper.innerHTML = sanitizedHtml;
return tagsWrapper;
}
function renderAndSanitizeMarkdownToHtml(content: string, domSanitizer: DomSanitizer): string {
const markedHtml = marked(content) as string;
const sanitizedHtml = domSanitizer.sanitize(SecurityContext.HTML, markedHtml) ?? '';
return sanitizedHtml;
}
@@ -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;
@@ -25,12 +25,14 @@
border-block-end: 1px solid var(--senary-contrast);
padding-block-end: 2rem;
margin-block-end: 1rem;
-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;
@@ -44,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);
@@ -87,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;
@@ -107,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;
}
}
@@ -1,4 +1,4 @@
<div class="page docs-viewer">
<main class="page docs-viewer">
<h1 class="page-header" tabindex="-1">Update Guide</h1>
<div class="wizard">
<div>
@@ -205,4 +205,4 @@
}
</div>
}
</div>
</main>
@@ -1,12 +1,12 @@
@use '@angular/docs/styles/media-queries' as mq;
$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 {
@@ -108,7 +108,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,27 @@ 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: 'Async operations',
path: 'guide/forms/signals/async-operations',
@@ -660,6 +681,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 +885,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,
},
],
},
@@ -875,7 +898,6 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
},
{
label: 'Build with AI',
status: 'new',
children: [
{
label: 'Get Started',
@@ -888,9 +910,10 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
contentPath: 'ai/develop-with-ai',
},
{
label: 'Design Patterns',
path: 'ai/design-patterns',
contentPath: 'ai/design-patterns',
label: 'Agent Skills',
path: 'ai/agent-skills',
contentPath: 'ai/agent-skills',
status: 'new',
},
{
label: 'Angular CLI MCP Server setup',
@@ -902,6 +925,11 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
path: 'ai/ai-tutor',
contentPath: 'ai/ai-tutor',
},
{
label: 'Design Patterns',
path: 'ai/design-patterns',
contentPath: 'ai/design-patterns',
},
],
},
{
@@ -1037,16 +1065,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',
},
],
},
{
+2 -7
View File
@@ -128,13 +128,8 @@ export const REDIRECT_ROUTES: Route[] = [
redirectTo: '/tools/cli/build-system-migration#hot-module-replacement',
},
{
path: 'guide',
children: [
{
path: 'pipes',
redirectTo: '/guide/templates/pipes',
},
],
path: 'guide/pipes',
redirectTo: '/guide/templates/pipes',
},
{
path: 'guide/experimental/zoneless',
Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

+24
View File
@@ -0,0 +1,24 @@
# Agent Skills
Agent Skills are specialized, domain-specific instructions and capabilities designed for AI agents like Gemini CLI. These skills provide architectural guidance, generate idiomatic Angular code, and help scaffold new projects using modern best practices.
By using Agent Skills, you can ensure that the AI agent you are working with has the most up-to-date information about Angular's conventions, reactivity models (like Signals), and project structure.
## Available Skills
The Angular team maintains a collection of official skills that are regularly updated to stay in sync with the latest framework improvements.
| Skill | Description |
| :---------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **`angular-developer`** | Generates Angular code and provides architectural guidance. Useful for creating components, services, or obtaining best practices on reactivity (signals, linkedSignal, resource), forms, dependency injection, routing, SSR, accessibility (ARIA), animations, styling, testing, or CLI tooling. |
| **`angular-new-app`** | Creates a new Angular app using the Angular CLI. Provides important guidelines for effectively setting up and structuring a modern Angular application. |
## Using Agent Skills
Agent Skills are designed to be used with agentic coding tools like [Gemini CLI](https://geminicli.com/docs/cli/skills/), [Antigravity](https://antigravity.google/docs/skills) and more. Activating a skill loads the specific instructions and resources needed for that task.
To use these skills in your own environment you may follow the instructions for your specific tool or use a community tool like [skills.sh](https://skills.sh/).
```bash
npx skills add https://github.com/angular/skills
```
+1
View File
@@ -115,4 +115,5 @@ To learn about LLM prompts and AI IDE setup, see the following guides:
<docs-pill-row>
<docs-pill href="ai/develop-with-ai" title="LLM prompts and IDE setup"/>
<docs-pill href="ai/agent-skills" title="Agent Skills"/>
</docs-pill-row>
+1 -1
View File
@@ -1,4 +1,4 @@
{
"branchName": "refs/heads/21.2.x",
"sha": "a076ff7f05b678c4cdc6b85dbfe474a09f56022a"
"sha": "0707c69d63e9962bab077dbc45770c25c8d26b7c"
}
+87 -48
View File
@@ -44,16 +44,6 @@
"description": "A global unique identifier for the panel.",
"jsdocTags": []
},
{
"name": "panelId",
"type": "InputSignal<string>",
"memberType": "property",
"memberTags": [
"readonly"
],
"description": "A local unique identifier for the panel, used to match with its trigger's `panelId`.",
"jsdocTags": []
},
{
"name": "visible",
"type": "Signal<boolean>",
@@ -165,7 +155,7 @@
}
],
"generics": [],
"description": "The content panel of an accordion item that is conditionally visible.\n\nThis directive is a container for the content that is shown or hidden. It requires\na `panelId` that must match the `panelId` of its corresponding `ngAccordionTrigger`.\nThe content within the panel should be provided using an `ng-template` with the\n`ngAccordionContent` directive so that the content is not rendered on the page until the trigger\nis expanded. It applies `role=\"region\"` for accessibility and uses the `inert` attribute to hide\nits content from assistive technologies when not visible.\n\n```html\n<div ngAccordionPanel panelId=\"unique-id-1\">\n <ng-template ngAccordionContent>\n <p>This content is lazily rendered and will be shown when the panel is expanded.</p>\n </ng-template>\n</div>\n```",
"description": "The content panel of an accordion item that is conditionally visible.\n\nThis directive is a container for the content that is shown or hidden. It should\nexpose a template reference that will be used by the corresponding `ngAccordionTrigger`.\nThe content within the panel should be provided using an `ng-template` with the\n`ngAccordionContent` directive so that the content is not rendered on the page until the trigger\nis expanded. It applies `role=\"region\"` for accessibility and uses the `inert` attribute to hide\nits content from assistive technologies when not visible.\n\n```html\n<div ngAccordionPanel #panel=\"ngAccordionPanel\">\n <ng-template ngAccordionContent>\n <p>This content is lazily rendered and will be shown when the panel is expanded.</p>\n </ng-template>\n</div>\n```",
"jsdocTags": [
{
"name": "developerPreview",
@@ -176,12 +166,12 @@
"comment": "[Accordion](guide/aria/accordion)"
}
],
"rawComment": "/**\n * The content panel of an accordion item that is conditionally visible.\n *\n * This directive is a container for the content that is shown or hidden. It requires\n * a `panelId` that must match the `panelId` of its corresponding `ngAccordionTrigger`.\n * The content within the panel should be provided using an `ng-template` with the\n * `ngAccordionContent` directive so that the content is not rendered on the page until the trigger\n * is expanded. It applies `role=\"region\"` for accessibility and uses the `inert` attribute to hide\n * its content from assistive technologies when not visible.\n *\n * ```html\n * <div ngAccordionPanel panelId=\"unique-id-1\">\n * <ng-template ngAccordionContent>\n * <p>This content is lazily rendered and will be shown when the panel is expanded.</p>\n * </ng-template>\n * </div>\n * ```\n *\n * @developerPreview 21.0\n * @see [Accordion](guide/aria/accordion)\n */",
"rawComment": "/**\n * The content panel of an accordion item that is conditionally visible.\n *\n * This directive is a container for the content that is shown or hidden. It should\n * expose a template reference that will be used by the corresponding `ngAccordionTrigger`.\n * The content within the panel should be provided using an `ng-template` with the\n * `ngAccordionContent` directive so that the content is not rendered on the page until the trigger\n * is expanded. It applies `role=\"region\"` for accessibility and uses the `inert` attribute to hide\n * its content from assistive technologies when not visible.\n *\n * ```html\n * <div ngAccordionPanel #panel=\"ngAccordionPanel\">\n * <ng-template ngAccordionContent>\n * <p>This content is lazily rendered and will be shown when the panel is expanded.</p>\n * </ng-template>\n * </div>\n * ```\n *\n * @developerPreview 21.0\n * @see [Accordion](guide/aria/accordion)\n */",
"implements": [],
"source": {
"filePath": "/src/aria/accordion/accordion-panel.ts",
"startLine": 42,
"endLine": 103
"startLine": 34,
"endLine": 88
}
},
{
@@ -199,6 +189,19 @@
"description": "A reference to the trigger element.",
"jsdocTags": []
},
{
"name": "panel",
"type": "InputSignal<AccordionPanel>",
"memberType": "property",
"memberTags": [
"readonly",
"input"
],
"description": "The associated AccordionPanel.",
"jsdocTags": [],
"inputAlias": "panel",
"isRequiredInput": true
},
{
"name": "id",
"type": "InputSignal<any>",
@@ -207,23 +210,20 @@
"readonly",
"input"
],
"description": "A unique identifier for the widget.",
"description": "The unique identifier for the trigger.",
"jsdocTags": [],
"inputAlias": "id",
"isRequiredInput": false
},
{
"name": "panelId",
"type": "InputSignal<string>",
"type": "Signal<any>",
"memberType": "property",
"memberTags": [
"readonly",
"input"
"readonly"
],
"description": "A local unique identifier for the trigger, used to match with its panel's `panelId`.",
"jsdocTags": [],
"inputAlias": "panelId",
"isRequiredInput": true
"description": "The unique identifier for the correspondingtrigger panel.",
"jsdocTags": []
},
{
"name": "disabled",
@@ -263,6 +263,39 @@
"description": "Whether the trigger is active.",
"jsdocTags": []
},
{
"name": "ngOnInit",
"signatures": [
{
"name": "ngOnInit",
"entryType": "function",
"description": "",
"generics": [],
"isNewType": false,
"jsdocTags": [],
"params": [],
"rawComment": "",
"returnType": "void"
}
],
"implementation": {
"params": [],
"isNewType": false,
"returnType": "void",
"generics": [],
"name": "ngOnInit",
"description": "",
"entryType": "function",
"jsdocTags": [],
"rawComment": ""
},
"entryType": "function",
"description": "",
"jsdocTags": [],
"rawComment": "",
"memberType": "method",
"memberTags": []
},
{
"name": "expand",
"signatures": [
@@ -364,7 +397,7 @@
}
],
"generics": [],
"description": "The trigger that toggles the visibility of its associated `ngAccordionPanel`.\n\nThis directive requires a `panelId` that must match the `panelId` of the `ngAccordionPanel` it\ncontrols. When clicked, it will expand or collapse the panel. It also handles keyboard\ninteractions for navigation within the `ngAccordionGroup`. It applies `role=\"button\"` and manages\n`aria-expanded`, `aria-controls`, and `aria-disabled` attributes for accessibility.\nThe `disabled` input can be used to disable the trigger.\n\n```html\n<button ngAccordionTrigger panelId=\"unique-id-1\">\n Accordion Trigger Text\n</button>\n```",
"description": "The trigger that toggles the visibility of its associated `ngAccordionPanel`.\n\nThis directive requires the `panel` input be set to the template reference of the `ngAccordionPanel`\nit controls. When clicked, it will expand or collapse the panel. It also handles keyboard\ninteractions for navigation within the `ngAccordionGroup`. It applies `role=\"button\"` and manages\n`aria-expanded`, `aria-controls`, and `aria-disabled` attributes for accessibility.\nThe `disabled` input can be used to disable the trigger.\n\n```html\n<button ngAccordionTrigger [panel]=\"panel\">\n Accordion Trigger Text\n</button>\n```",
"jsdocTags": [
{
"name": "developerPreview",
@@ -375,8 +408,10 @@
"comment": "[Accordion](guide/aria/accordion)"
}
],
"rawComment": "/**\n * The trigger that toggles the visibility of its associated `ngAccordionPanel`.\n *\n * This directive requires a `panelId` that must match the `panelId` of the `ngAccordionPanel` it\n * controls. When clicked, it will expand or collapse the panel. It also handles keyboard\n * interactions for navigation within the `ngAccordionGroup`. It applies `role=\"button\"` and manages\n * `aria-expanded`, `aria-controls`, and `aria-disabled` attributes for accessibility.\n * The `disabled` input can be used to disable the trigger.\n *\n * ```html\n * <button ngAccordionTrigger panelId=\"unique-id-1\">\n * Accordion Trigger Text\n * </button>\n * ```\n *\n * @developerPreview 21.0\n * @see [Accordion](guide/aria/accordion)\n */",
"implements": [],
"rawComment": "/**\n * The trigger that toggles the visibility of its associated `ngAccordionPanel`.\n *\n * This directive requires the `panel` input be set to the template reference of the `ngAccordionPanel`\n * it controls. When clicked, it will expand or collapse the panel. It also handles keyboard\n * interactions for navigation within the `ngAccordionGroup`. It applies `role=\"button\"` and manages\n * `aria-expanded`, `aria-controls`, and `aria-disabled` attributes for accessibility.\n * The `disabled` input can be used to disable the trigger.\n *\n * ```html\n * <button ngAccordionTrigger [panel]=\"panel\">\n * Accordion Trigger Text\n * </button>\n * ```\n *\n * @developerPreview 21.0\n * @see [Accordion](guide/aria/accordion)\n */",
"implements": [
"OnInit"
],
"isStandalone": true,
"selector": "[ngAccordionTrigger]",
"exportAs": [
@@ -388,7 +423,7 @@
"source": {
"filePath": "/src/aria/accordion/accordion-trigger.ts",
"startLine": 42,
"endLine": 107
"endLine": 112
}
},
{
@@ -536,7 +571,7 @@
}
],
"generics": [],
"description": "A container for a group of accordion items. It manages the overall state and\ninteractions of the accordion, such as keyboard navigation and expansion mode.\n\nThe `ngAccordionGroup` serves as the root of a group of accordion triggers and panels,\ncoordinating the behavior of the `ngAccordionTrigger` and `ngAccordionPanel` elements within it.\nIt supports both single and multiple expansion modes.\n\n```html\n<div ngAccordionGroup [multiExpandable]=\"true\" [(expandedPanels)]=\"expandedItems\">\n <div class=\"accordion-item\">\n <h3>\n <button ngAccordionTrigger panelId=\"item-1\">Item 1</button>\n </h3>\n <div ngAccordionPanel panelId=\"item-1\">\n <ng-template ngAccordionContent>\n <p>Content for Item 1.</p>\n </ng-template>\n </div>\n </div>\n <div class=\"accordion-item\">\n <h3>\n <button ngAccordionTrigger panelId=\"item-2\">Item 2</button>\n </h3>\n <div ngAccordionPanel panelId=\"item-2\">\n <ng-template ngAccordionContent>\n <p>Content for Item 2.</p>\n </ng-template>\n </div>\n </div>\n</div>\n```",
"description": "A container for a group of accordion items. It manages the overall state and\ninteractions of the accordion, such as keyboard navigation and expansion mode.\n\nThe `ngAccordionGroup` serves as the root of a group of accordion triggers and panels,\ncoordinating the behavior of the `ngAccordionTrigger` and `ngAccordionPanel` elements within it.\nIt supports both single and multiple expansion modes.\n\n```html\n<div ngAccordionGroup [multiExpandable]=\"true\">\n <div class=\"accordion-item\">\n <h3>\n <button ngAccordionTrigger [panel]=\"panel1\">Item 1</button>\n </h3>\n <div ngAccordionPanel #panel1=\"ngAccordionPanel\">\n <ng-template ngAccordionContent>\n <p>Content for Item 1.</p>\n </ng-template>\n </div>\n </div>\n <div class=\"accordion-item\">\n <h3>\n <button ngAccordionTrigger [panel]=\"panel2\">Item 2</button>\n </h3>\n <div ngAccordionPanel #panel2=\"ngAccordionPanel\">\n <ng-template ngAccordionContent>\n <p>Content for Item 2.</p>\n </ng-template>\n </div>\n </div>\n</div>\n```",
"jsdocTags": [
{
"name": "developerPreview",
@@ -547,7 +582,7 @@
"comment": "[Accordion](guide/aria/accordion)"
}
],
"rawComment": "/**\n * A container for a group of accordion items. It manages the overall state and\n * interactions of the accordion, such as keyboard navigation and expansion mode.\n *\n * The `ngAccordionGroup` serves as the root of a group of accordion triggers and panels,\n * coordinating the behavior of the `ngAccordionTrigger` and `ngAccordionPanel` elements within it.\n * It supports both single and multiple expansion modes.\n *\n * ```html\n * <div ngAccordionGroup [multiExpandable]=\"true\" [(expandedPanels)]=\"expandedItems\">\n * <div class=\"accordion-item\">\n * <h3>\n * <button ngAccordionTrigger panelId=\"item-1\">Item 1</button>\n * </h3>\n * <div ngAccordionPanel panelId=\"item-1\">\n * <ng-template ngAccordionContent>\n * <p>Content for Item 1.</p>\n * </ng-template>\n * </div>\n * </div>\n * <div class=\"accordion-item\">\n * <h3>\n * <button ngAccordionTrigger panelId=\"item-2\">Item 2</button>\n * </h3>\n * <div ngAccordionPanel panelId=\"item-2\">\n * <ng-template ngAccordionContent>\n * <p>Content for Item 2.</p>\n * </ng-template>\n * </div>\n * </div>\n * </div>\n * ```\n *\n * @developerPreview 21.0\n * @see [Accordion](guide/aria/accordion)\n */",
"rawComment": "/**\n * A container for a group of accordion items. It manages the overall state and\n * interactions of the accordion, such as keyboard navigation and expansion mode.\n *\n * The `ngAccordionGroup` serves as the root of a group of accordion triggers and panels,\n * coordinating the behavior of the `ngAccordionTrigger` and `ngAccordionPanel` elements within it.\n * It supports both single and multiple expansion modes.\n *\n * ```html\n * <div ngAccordionGroup [multiExpandable]=\"true\">\n * <div class=\"accordion-item\">\n * <h3>\n * <button ngAccordionTrigger [panel]=\"panel1\">Item 1</button>\n * </h3>\n * <div ngAccordionPanel #panel1=\"ngAccordionPanel\">\n * <ng-template ngAccordionContent>\n * <p>Content for Item 1.</p>\n * </ng-template>\n * </div>\n * </div>\n * <div class=\"accordion-item\">\n * <h3>\n * <button ngAccordionTrigger [panel]=\"panel2\">Item 2</button>\n * </h3>\n * <div ngAccordionPanel #panel2=\"ngAccordionPanel\">\n * <ng-template ngAccordionContent>\n * <p>Content for Item 2.</p>\n * </ng-template>\n * </div>\n * </div>\n * </div>\n * ```\n *\n * @developerPreview 21.0\n * @see [Accordion](guide/aria/accordion)\n */",
"implements": [],
"isStandalone": true,
"selector": "[ngAccordionGroup]",
@@ -559,8 +594,8 @@
],
"source": {
"filePath": "src/aria/accordion/accordion-group.ts",
"startLine": 62,
"endLine": 158
"startLine": 60,
"endLine": 120
}
}
],
@@ -569,28 +604,20 @@
"Directive",
"@angular/core"
],
[
"input",
"@angular/core"
],
[
"inject",
"@angular/core"
],
[
"afterRenderEffect",
"@angular/core"
],
[
"signal",
"@angular/core"
],
[
"computed",
"@angular/core"
],
[
"WritableSignal",
"inject",
"@angular/core"
],
[
"input",
"@angular/core"
],
[
@@ -602,17 +629,25 @@
"@angular/core"
],
[
"model",
"OnInit",
"@angular/core"
],
[
"booleanAttribute",
"@angular/core"
],
[
"model",
"@angular/core"
],
[
"contentChildren",
"@angular/core"
],
[
"signal",
"@angular/core"
],
[
"Directionality",
"@angular/cdk/bidi"
@@ -645,10 +680,6 @@
"AccordionPanel.id",
"@angular/aria/accordion"
],
[
"AccordionPanel.panelId",
"@angular/aria/accordion"
],
[
"AccordionPanel.visible",
"@angular/aria/accordion"
@@ -673,6 +704,10 @@
"AccordionTrigger.element",
"@angular/aria/accordion"
],
[
"AccordionTrigger.panel",
"@angular/aria/accordion"
],
[
"AccordionTrigger.id",
"@angular/aria/accordion"
@@ -693,6 +728,10 @@
"AccordionTrigger.active",
"@angular/aria/accordion"
],
[
"AccordionTrigger.ngOnInit",
"@angular/aria/accordion"
],
[
"AccordionTrigger.expand",
"@angular/aria/accordion"
+1 -1
View File
@@ -1,4 +1,4 @@
{
"branchName": "refs/heads/21.2.x",
"sha": "fc8b94d631a5d105a87abc2a008a6e8f76d7f3f5"
"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"
@@ -1142,8 +1142,8 @@
"implements": [],
"source": {
"filePath": "/src/cdk/testing/selenium-webdriver/selenium-web-driver-harness-environment.ts",
"startLine": 35,
"endLine": 38
"startLine": 34,
"endLine": 37
}
},
{
@@ -1194,8 +1194,8 @@
"rawComment": "/** Waits for angular to be ready after the page load. */",
"source": {
"filePath": "/src/cdk/testing/selenium-webdriver/selenium-web-driver-harness-environment.ts",
"startLine": 65,
"endLine": 68
"startLine": 66,
"endLine": 69
}
},
{
@@ -1635,8 +1635,8 @@
"implements": [],
"source": {
"filePath": "/src/cdk/testing/selenium-webdriver/selenium-web-driver-harness-environment.ts",
"startLine": 71,
"endLine": 150
"startLine": 72,
"endLine": 151
}
}
],
+1 -1
View File
@@ -1,4 +1,4 @@
{
"branchName": "refs/heads/21.2.x",
"sha": "8e9555870edb3275b35d299613ef3cf9d3bfb73a"
"sha": "a8aca91bb6d490f66b6bdc873e3e350b9a1addad"
}
+2 -2
View File
@@ -109,7 +109,7 @@
"aliases": [
"s"
],
"description": "Include the styles for the root component directly within the `app.component.ts` file. Only CSS styles can be included inline. By default, a separate stylesheet file (e.g., `app.component.css`) is created."
"description": "Include the styles for the root component directly within the `app.ts` file. Only CSS styles can be included inline. By default, a separate stylesheet file (e.g., `app.css`) is created."
},
{
"name": "inline-template",
@@ -117,7 +117,7 @@
"aliases": [
"t"
],
"description": "Include the HTML template for the root component directly within the `app.component.ts` file. By default, a separate template file (e.g., `app.component.html`) is created."
"description": "Include the HTML template for the root component directly within the `app.ts` file. By default, a separate template file (e.g., `app.html`) is created."
},
{
"name": "minimal",
+2 -2
View File
@@ -91,7 +91,7 @@
"aliases": [
"s"
],
"description": "Include the styles for the initial application's root component directly within the `app.component.ts` file. By default, a separate stylesheet file (e.g., `app.component.css`) is created."
"description": "Include the styles for the initial application's root component directly within the `app.ts` file. By default, a separate stylesheet file (e.g., `app.css`) is created."
},
{
"name": "inline-template",
@@ -99,7 +99,7 @@
"aliases": [
"t"
],
"description": "Include the HTML template for the initial application's root component directly within the `app.component.ts` file. By default, a separate template file (e.g., `app.component.html`) is created."
"description": "Include the HTML template for the initial application's root component directly within the `app.ts` file. By default, a separate template file (e.g., `app.html`) is created."
},
{
"name": "interactive",
+1 -2
View File
@@ -35,8 +35,7 @@
{
"name": "coverage",
"type": "boolean",
"default": false,
"description": "Enables coverage reporting for tests."
"description": "Enables coverage reporting for tests. If not specified, the coverage configuration from a runner configuration file will be used if present. Otherwise, coverage is disabled by default."
},
{
"name": "coverage-exclude",
+1 -1
View File
@@ -3,7 +3,7 @@
"command": "ng update [packages..]",
"shortDescription": "Updates your workspace and its dependencies. See https://update.angular.dev/.",
"longDescriptionRelativePath": "@angular/cli/src/commands/update/long-description.md",
"longDescription": "Perform a basic update to the current stable release of the core framework and CLI by running the following command.\n\n```\nng update @angular/cli @angular/core\n```\n\nTo update to the next beta or pre-release version, use the `--next` option.\n\nTo update from one major version to another, use the format\n\n```\nng update @angular/cli@^<major_version> @angular/core@^<major_version>\n```\n\nWe recommend that you always update to the latest patch version, as it contains fixes we released since the initial major release.\nFor example, use the following command to take the latest 10.x.x version and use that to update.\n\n```\nng update @angular/cli@^10 @angular/core@^10\n```\n\nFor detailed information and guidance on updating your application, see the interactive [Angular Update Guide](https://update.angular.dev/).\n",
"longDescription": "Perform a basic update to the current stable release of the core framework and CLI by running the following command.\n\n```\nng update @angular/cli @angular/core\n```\n\nTo update to the next beta or pre-release version, use the `--next` option.\n\nTo update from one major version to another, use the format\n\n```\nng update @angular/cli@^<major_version> @angular/core@^<major_version>\n```\n\nWe recommend that you always update to the latest patch version, as it contains fixes we released since the initial major release.\nFor example, use the following command to take the latest 21.x.x version and use that to update.\n\n```\nng update @angular/cli@^21 @angular/core@^21\n```\n\nFor detailed information and guidance on updating your application, see the interactive [Angular Update Guide](/update-guide).\n",
"aliases": [],
"deprecated": false,
"options": [
@@ -10,7 +10,7 @@ There are some niche use cases when you may want to maintain a custom build pipe
- You have an existing app using a different toolchain and you’d like to add Angular to it
- You’re strongly coupled to [module federation](https://module-federation.io/) and unable to adopt bundler-agnostic [native federation](https://www.npmjs.com/package/@angular-architects/native-federation)
- You’d like to create an short-lived experiment using your favorite build tool
- You’d like to create a short-lived experiment using your favorite build tool
## What are the options?
+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"/>
@@ -1,6 +1,6 @@
<div ngAccordionGroup class="basic-accordion">
<h3>
<span ngAccordionTrigger panelId="q1" #trigger1="ngAccordionTrigger" [expanded]="true">
<span ngAccordionTrigger [panel]="panel1" #trigger1="ngAccordionTrigger" [expanded]="true">
Which attribute tells assistive tech whether the panel is open or closed?
<span
aria-hidden="true"
@@ -9,7 +9,7 @@
></span>
</span>
</h3>
<div ngAccordionPanel panelId="q1">
<div ngAccordionPanel #panel1="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use <code>aria-expanded</code> on the button element. Set it to "true" when the content
@@ -20,7 +20,7 @@
</div>
<h3>
<span ngAccordionTrigger panelId="q2" #trigger2="ngAccordionTrigger" disabled>
<span ngAccordionTrigger [panel]="panel2" #trigger2="ngAccordionTrigger" disabled>
How do you link the button to the content it controls?
<span
aria-hidden="true"
@@ -29,7 +29,7 @@
></span>
</span>
</h3>
<div ngAccordionPanel panelId="q2">
<div ngAccordionPanel #panel2="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use the <code>aria-controls</code> attribute on the button, and set its value to match the
@@ -40,7 +40,7 @@
</div>
<h3>
<span ngAccordionTrigger panelId="q3" #trigger3="ngAccordionTrigger">
<span ngAccordionTrigger [panel]="panel3" #trigger3="ngAccordionTrigger">
What role should the heading element containing the accordion button have?
<span
aria-hidden="true"
@@ -49,7 +49,7 @@
></span>
</span>
</h3>
<div ngAccordionPanel panelId="q3">
<div ngAccordionPanel #panel3="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
The element containing the button should typically have <code>role="heading"</code> with an
@@ -1,6 +1,6 @@
<div ngAccordionGroup class="material-accordion">
<h3>
<span ngAccordionTrigger panelId="q1" #trigger1="ngAccordionTrigger" [expanded]="true">
<span ngAccordionTrigger [panel]="panel1" #trigger1="ngAccordionTrigger" [expanded]="true">
Which attribute tells assistive tech whether the panel is open or closed?
<span
aria-hidden="true"
@@ -11,7 +11,7 @@
>
</span>
</h3>
<div ngAccordionPanel panelId="q1">
<div ngAccordionPanel #panel1="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use <code>aria-expanded</code> on the button element. Set it to "true" when the content
@@ -22,7 +22,7 @@
</div>
<h3>
<span ngAccordionTrigger panelId="q2" #trigger2="ngAccordionTrigger" disabled>
<span ngAccordionTrigger [panel]="panel2" #trigger2="ngAccordionTrigger" disabled>
How do you link the button to the content it controls?
<span
aria-hidden="true"
@@ -33,7 +33,7 @@
>
</span>
</h3>
<div ngAccordionPanel panelId="q2">
<div ngAccordionPanel #panel2="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use the <code>aria-controls</code> attribute on the button, and set its value to match the
@@ -44,7 +44,7 @@
</div>
<h3>
<span ngAccordionTrigger panelId="q3" #trigger3="ngAccordionTrigger">
<span ngAccordionTrigger [panel]="panel3" #trigger3="ngAccordionTrigger">
What role should the heading element containing the accordion button have?
<span
aria-hidden="true"
@@ -55,7 +55,7 @@
>
</span>
</h3>
<div ngAccordionPanel panelId="q3">
<div ngAccordionPanel #panel3="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
The element containing the button should typically have <code>role="heading"</code> with an
@@ -1,41 +1,41 @@
<div ngAccordionGroup class="retro-accordion">
<h3>
<span ngAccordionTrigger panelId="q1" #trigger1="ngAccordionTrigger">
<span ngAccordionTrigger [panel]="panel1" #trigger1="ngAccordionTrigger">
Unlock Treasure Box
<span aria-hidden="true" class="material-symbols-outlined" translate="no">{{
trigger1.expanded() ? 'lock_open_right' : 'lock'
}}</span>
</span>
</h3>
<div ngAccordionPanel panelId="q1">
<div ngAccordionPanel #panel1="ngAccordionPanel">
<ng-template ngAccordionContent>
<div class="treasure-box">👻</div>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger panelId="q2" #trigger2="ngAccordionTrigger" disabled>
<span ngAccordionTrigger [panel]="panel2" #trigger2="ngAccordionTrigger" disabled>
Unlock Treasure Box
<span aria-hidden="true" class="material-symbols-outlined" translate="no">{{
trigger2.expanded() ? 'lock_open_right' : 'lock'
}}</span>
</span>
</h3>
<div ngAccordionPanel panelId="q2">
<div ngAccordionPanel #panel2="ngAccordionPanel">
<ng-template ngAccordionContent>
<div class="treasure-box">💎💎💎</div>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger panelId="q3" #trigger3="ngAccordionTrigger" [expanded]="true">
<span ngAccordionTrigger [panel]="panel3" #trigger3="ngAccordionTrigger" [expanded]="true">
Unlock Treasure Box
<span aria-hidden="true" class="material-symbols-outlined" translate="no">{{
trigger3.expanded() ? 'lock_open_right' : 'lock'
}}</span>
</span>
</h3>
<div ngAccordionPanel panelId="q3">
<div ngAccordionPanel #panel3="ngAccordionPanel">
<ng-template ngAccordionContent>
<div class="treasure-box">🐸 ribbit...ribbit...</div>
</ng-template>
@@ -1,6 +1,6 @@
<div ngAccordionGroup class="basic-accordion">
<h3>
<span ngAccordionTrigger panelId="q1" #trigger1="ngAccordionTrigger" [expanded]="true">
<span ngAccordionTrigger [panel]="panel1" #trigger1="ngAccordionTrigger" [expanded]="true">
Which attribute tells assistive tech whether the panel is open or closed?
<span
aria-hidden="true"
@@ -9,7 +9,7 @@
></span>
</span>
</h3>
<div ngAccordionPanel panelId="q1">
<div ngAccordionPanel #panel1="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use <code>aria-expanded</code> on the button element. Set it to "true" when the content
@@ -20,7 +20,7 @@
</div>
<h3>
<span ngAccordionTrigger panelId="q2" #trigger2="ngAccordionTrigger">
<span ngAccordionTrigger [panel]="panel2" #trigger2="ngAccordionTrigger">
How do you link the button to the content it controls?
<span
aria-hidden="true"
@@ -29,7 +29,7 @@
></span>
</span>
</h3>
<div ngAccordionPanel panelId="q2">
<div ngAccordionPanel #panel2="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use the <code>aria-controls</code> attribute on the button, and set its value to match the
@@ -40,7 +40,7 @@
</div>
<h3>
<span ngAccordionTrigger panelId="q3" #trigger3="ngAccordionTrigger" [expanded]="true">
<span ngAccordionTrigger [panel]="panel3" #trigger3="ngAccordionTrigger" [expanded]="true">
What role should the heading element containing the accordion button have?
<span
aria-hidden="true"
@@ -49,7 +49,7 @@
></span>
</span>
</h3>
<div ngAccordionPanel panelId="q3">
<div ngAccordionPanel #panel3="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
The element containing the button should typically have <code>role="heading"</code> with an
@@ -1,6 +1,6 @@
<div ngAccordionGroup class="material-accordion">
<h3>
<span ngAccordionTrigger panelId="q1" #trigger1="ngAccordionTrigger" [expanded]="true">
<span ngAccordionTrigger [panel]="panel1" #trigger1="ngAccordionTrigger" [expanded]="true">
Which attribute tells assistive tech whether the panel is open or closed?
<span
aria-hidden="true"
@@ -11,7 +11,7 @@
>
</span>
</h3>
<div ngAccordionPanel panelId="q1">
<div ngAccordionPanel #panel1="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use <code>aria-expanded</code> on the button element. Set it to "true" when the content
@@ -22,7 +22,7 @@
</div>
<h3>
<span ngAccordionTrigger panelId="q2" #trigger2="ngAccordionTrigger">
<span ngAccordionTrigger [panel]="panel2" #trigger2="ngAccordionTrigger">
How do you link the button to the content it controls?
<span
aria-hidden="true"
@@ -33,7 +33,7 @@
>
</span>
</h3>
<div ngAccordionPanel panelId="q2">
<div ngAccordionPanel #panel2="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use the <code>aria-controls</code> attribute on the button, and set its value to match the
@@ -44,7 +44,7 @@
</div>
<h3>
<span ngAccordionTrigger panelId="q3" #trigger3="ngAccordionTrigger" [expanded]="true">
<span ngAccordionTrigger [panel]="panel3" #trigger3="ngAccordionTrigger" [expanded]="true">
What role should the heading element containing the accordion button have?
<span
aria-hidden="true"
@@ -55,7 +55,7 @@
>
</span>
</h3>
<div ngAccordionPanel panelId="q3">
<div ngAccordionPanel #panel3="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
The element containing the button should typically have <code>role="heading"</code> with an
@@ -1,41 +1,41 @@
<div ngAccordionGroup class="retro-accordion">
<h3>
<span ngAccordionTrigger panelId="q1" #trigger1="ngAccordionTrigger">
<span ngAccordionTrigger [panel]="panel1" #trigger1="ngAccordionTrigger">
Unlock Treasure Box
<span aria-hidden="true" class="material-symbols-outlined" translate="no">{{
trigger1.expanded() ? 'lock_open_right' : 'lock'
}}</span>
</span>
</h3>
<div ngAccordionPanel panelId="q1">
<div ngAccordionPanel #panel1="ngAccordionPanel">
<ng-template ngAccordionContent>
<div class="treasure-box">👻</div>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger panelId="q2" #trigger2="ngAccordionTrigger">
<span ngAccordionTrigger [panel]="panel2" #trigger2="ngAccordionTrigger">
Unlock Treasure Box
<span aria-hidden="true" class="material-symbols-outlined" translate="no">{{
trigger2.expanded() ? 'lock_open_right' : 'lock'
}}</span>
</span>
</h3>
<div ngAccordionPanel panelId="q2">
<div ngAccordionPanel #panel2="ngAccordionPanel">
<ng-template ngAccordionContent>
<div class="treasure-box">💎💎💎</div>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger panelId="q3" #trigger3="ngAccordionTrigger">
<span ngAccordionTrigger [panel]="panel3" #trigger3="ngAccordionTrigger">
Unlock Treasure Box
<span aria-hidden="true" class="material-symbols-outlined" translate="no">{{
trigger3.expanded() ? 'lock_open_right' : 'lock'
}}</span>
</span>
</h3>
<div ngAccordionPanel panelId="q3">
<div ngAccordionPanel #panel3="ngAccordionPanel">
<ng-template ngAccordionContent>
<div class="treasure-box">🐸 ribbit...ribbit...</div>
</ng-template>
@@ -1,6 +1,6 @@
<div ngAccordionGroup class="basic-accordion" [multiExpandable]="false">
<h3>
<span ngAccordionTrigger panelId="q1" #trigger1="ngAccordionTrigger" [expanded]="true">
<span ngAccordionTrigger [panel]="panel1" #trigger1="ngAccordionTrigger" [expanded]="true">
Which attribute tells assistive tech whether the panel is open or closed?
<span
aria-hidden="true"
@@ -9,7 +9,7 @@
></span>
</span>
</h3>
<div ngAccordionPanel panelId="q1">
<div ngAccordionPanel #panel1="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use <code>aria-expanded</code> on the button element. Set it to "true" when the content
@@ -20,7 +20,7 @@
</div>
<h3>
<span ngAccordionTrigger panelId="q2" #trigger2="ngAccordionTrigger">
<span ngAccordionTrigger [panel]="panel2" #trigger2="ngAccordionTrigger">
How do you link the button to the content it controls?
<span
aria-hidden="true"
@@ -29,7 +29,7 @@
></span>
</span>
</h3>
<div ngAccordionPanel panelId="q2">
<div ngAccordionPanel #panel2="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use the <code>aria-controls</code> attribute on the button, and set its value to match the
@@ -40,7 +40,7 @@
</div>
<h3>
<span ngAccordionTrigger panelId="q3" #trigger3="ngAccordionTrigger">
<span ngAccordionTrigger [panel]="panel3" #trigger3="ngAccordionTrigger">
What role should the heading element containing the accordion button have?
<span
aria-hidden="true"
@@ -49,7 +49,7 @@
></span>
</span>
</h3>
<div ngAccordionPanel panelId="q3">
<div ngAccordionPanel #panel3="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
The element containing the button should typically have <code>role="heading"</code> with an
@@ -1,6 +1,6 @@
<div ngAccordionGroup class="material-accordion" [multiExpandable]="false">
<h3>
<span ngAccordionTrigger panelId="q1" #trigger1="ngAccordionTrigger">
<span ngAccordionTrigger [panel]="panel1" #trigger1="ngAccordionTrigger">
Which attribute tells assistive tech whether the panel is open or closed?
<span
aria-hidden="true"
@@ -11,7 +11,7 @@
>
</span>
</h3>
<div ngAccordionPanel panelId="q1">
<div ngAccordionPanel #panel1="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use <code>aria-expanded</code> on the button element. Set it to "true" when the content
@@ -22,7 +22,7 @@
</div>
<h3>
<span ngAccordionTrigger panelId="q2" #trigger2="ngAccordionTrigger" [expanded]="true">
<span ngAccordionTrigger [panel]="panel2" #trigger2="ngAccordionTrigger" [expanded]="true">
How do you link the button to the content it controls?
<span
aria-hidden="true"
@@ -33,7 +33,7 @@
>
</span>
</h3>
<div ngAccordionPanel panelId="q2">
<div ngAccordionPanel #panel2="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
Use the <code>aria-controls</code> attribute on the button, and set its value to match the
@@ -44,7 +44,7 @@
</div>
<h3>
<span ngAccordionTrigger panelId="q3" #trigger3="ngAccordionTrigger">
<span ngAccordionTrigger [panel]="panel3" #trigger3="ngAccordionTrigger">
What role should the heading element containing the accordion button have?
<span
aria-hidden="true"
@@ -55,7 +55,7 @@
>
</span>
</h3>
<div ngAccordionPanel panelId="q3">
<div ngAccordionPanel #panel3="ngAccordionPanel">
<ng-template ngAccordionContent>
<p>
The element containing the button should typically have <code>role="heading"</code> with an
@@ -1,41 +1,41 @@
<div ngAccordionGroup class="retro-accordion" [multiExpandable]="false">
<h3>
<span ngAccordionTrigger panelId="q1" #trigger1="ngAccordionTrigger">
<span ngAccordionTrigger [panel]="panel1" #trigger1="ngAccordionTrigger">
Unlock Treasure Box
<span aria-hidden="true" class="material-symbols-outlined" translate="no">{{
trigger1.expanded() ? 'lock_open_right' : 'lock'
}}</span>
</span>
</h3>
<div ngAccordionPanel panelId="q1">
<div ngAccordionPanel #panel1="ngAccordionPanel">
<ng-template ngAccordionContent>
<div class="treasure-box">👻</div>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger panelId="q2" #trigger2="ngAccordionTrigger">
<span ngAccordionTrigger [panel]="panel2" #trigger2="ngAccordionTrigger">
Unlock Treasure Box
<span aria-hidden="true" class="material-symbols-outlined" translate="no">{{
trigger2.expanded() ? 'lock_open_right' : 'lock'
}}</span>
</span>
</h3>
<div ngAccordionPanel panelId="q2">
<div ngAccordionPanel #panel2="ngAccordionPanel">
<ng-template ngAccordionContent>
<div class="treasure-box">💎💎💎</div>
</ng-template>
</div>
<h3>
<span ngAccordionTrigger panelId="q3" #trigger3="ngAccordionTrigger">
<span ngAccordionTrigger [panel]="panel3" #trigger3="ngAccordionTrigger">
Unlock Treasure Box
<span aria-hidden="true" class="material-symbols-outlined" translate="no">{{
trigger3.expanded() ? 'lock_open_right' : 'lock'
}}</span>
</span>
</h3>
<div ngAccordionPanel panelId="q3">
<div ngAccordionPanel #panel3="ngAccordionPanel">
<ng-template ngAccordionContent>
<div class="treasure-box">🐸 ribbit...ribbit...</div>
</ng-template>

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