Compare commits

..

471 Commits

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

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

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

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

Fixes #68317

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

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

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

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

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

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

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

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

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

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

This change adds specialized overloads for explicit read generics.

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

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

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

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

Closes #68418

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

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

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

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

Fixes #67928

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

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

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

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

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

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

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

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

Fixes #65938

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

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

fixes #68404

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

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

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

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

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

Fixes #68322

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

Fixed #68375

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

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

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

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

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

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

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

Fixes #67328

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

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

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

Fixed #68378

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

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

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

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

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

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

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

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

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

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

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

Fixes #67549

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

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

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

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

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

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

Fixed #59224

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

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

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

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

Closes #65657

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

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

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

The simple code that reproduces the behavior that exists now:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Closes #49610

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

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

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

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

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

Fixes #65310

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

fix #67650

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Relates to #67704.

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

Fixes #68039.

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

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

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

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

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

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

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

Fixes #68005

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

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

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

Fixes #67759

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

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

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

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

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

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

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

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

fixes #65491

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

(cherry picked from commit c15e3a005d)
2026-04-01 20:46:05 +02:00
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
Andrew Scott e7593653be release: cut the v21.2.3 release 2026-03-11 12:03:13 -07:00
JoostK 21b1c3b2ee fix(core): include signal debug names in their toString() representation
The `toString()` implementations in the primitives package intended to include
the debug name, yet the debug name was evaluated during construction before it
could ever have been assigned. This commit fixes that.

The Angular wrappers override the `toString()` representation to evaluate signals
ad-hoc instead of showing their internal state, and this commit aligns their
behavior to include the debug name in `toString` as well.

(cherry picked from commit b401c18674)
2026-03-11 18:28:39 +00:00
SkyZeroZx 6c73aaca30 refactor(common): Removes unused generic type parameters from KeyValueDiffers
Remove unused generic type parameters from KeyValueDiffers methods

(cherry picked from commit 0837d25a70)
2026-03-11 17:49:15 +00:00
Matthieu Riegler c98eab794c refactor(core): remove old resource params
G3 has been cleaned up we can drop those.

(cherry picked from commit 4842f5a3b7)
2026-03-11 17:44:16 +00:00
Thomas Willberger d45fced21b docs: remove duplicate aria-hidden attribute in angular aria ngTree example
(cherry picked from commit eafce6ed09)
2026-03-11 17:03:13 +00:00
SkyZeroZx ed48c41f65 refactor(compiler-cli): simplifies IncrementalCompilation.fresh
Removes the `ts.Program` argument from the `IncrementalCompilation.fresh` method.

(cherry picked from commit 66e72efef4)
2026-03-11 16:41:05 +00:00
Ibrahim Hussien 75135586d6 docs: combine multiple documentation improvements into one PR
(cherry picked from commit 903d51e855)
2026-03-11 16:29:49 +00:00
Angular Robot ff697707ba build: update pnpm to v10.32.0
See associated pull request for more information.
2026-03-11 09:28:49 -07:00
SkyZeroZx 7d76103999 docs(docs-infra): fix close button clickability and prevent layout shift
Adjust the z-index to ensure the close button remains clickable.

Also remove an unnecessary `position: relative` that was causing
layout shifts in the docs UI, which resulted in a visible and
unintended layout movement.

(cherry picked from commit 3ce1d9a755)
2026-03-11 16:28:10 +00:00
Matthieu Riegler 575f3023c1 refactor(core): interface cleanup
Should be fine to land this time.

(cherry picked from commit a675950e44)
2026-03-10 19:44:36 +00:00
Andrew Scott 4c86ed382d refactor(compiler-cli): abstract type check block metadata to be AST-free
This commit refactors the template type checking metadata interfaces to use detached, serializable metadata rather than retaining direct references to ts.Node or ts.Declaration instances.

A new tcb_adapter translates traditional TypeScript AST-bound metadata into these decoupled structures. This abstraction lays the groundwork for supporting native preprocessors (such as Rust or ts-go) which serialize metadata over JSON rather than passing live TypeScript objects.

Key changes:
- Introduced TcbDirectiveMetadata, TcbComponentMetadata, TcbReferenceMetadata, and TcbPipeMetadata to replace TypeCheckableDirectiveMeta where appropriate.
- Substituted deep TS compilation AST references with string module names and source spans to preserve out-of-band diagnostic capabilities.
- Detached generic typeParameters and transformType properties into synthesized, standalone TS mappings.
- Updated generateTypeCheckBlock and corresponding Operations to consume the new metadata.
2026-03-10 12:39:18 -07:00
Kristiyan Kostadinov 224e60ecb1 fix(core): sanitize translated attribute bindings with interpolations
Fixes that we weren't sanitizing attribute bindings with interpolations if they're marked for translation, for example: `<a href="{{evilLink}}" i18n-href></a>`.

Also adds a bit more test coverage for our sanitization.

(cherry picked from commit 8630319f74)
2026-03-10 18:13:54 +00:00
Kristiyan Kostadinov 492e7560e6 build: include localize in dev app
Includes `$localize` in the dev app so it's easier to test some code paths.

(cherry picked from commit f80ac30875)
2026-03-10 18:13:53 +00:00
Jon Snow 09638ec7c3 docs(core): clarify provideZoneChangeDetection usage in v21+
Clarify that provideZoneChangeDetection() is used to opt applications into NgZone/ZoneJS-based change detection and to configure NgZone options such as eventCoalescing.

Fixes #67498

(cherry picked from commit 196933863b)
2026-03-10 17:21:13 +00:00
Guseyn ab35383285 docs: fix first-app code getHousingLocationById housing.service.ts
1. Get location by Id work with `${this.url}/${id}`
2. Method getHousingLocationById returned empty object, api return object type, locationJson[0] ?? {} - give in result empty object.

(cherry picked from commit 9794e756f9)
2026-03-10 17:02:43 +00:00
Angular Robot 934e2732b3 build: lock file maintenance
See associated pull request for more information.
2026-03-10 09:59:30 -07:00
Guseyn c20dabda2a docs: fix first-app 13 step docs code row numbers
Fix docs code row numbers.

(cherry picked from commit 7bc7c57636)
2026-03-10 16:59:08 +00:00
Kristiyan Kostadinov 62a97f7e4b fix(core): ensure definitions compile
Includes the following changes to make sure the definitions for injectable compiler:
1. The types for the `factory` function now include the `parent` parameter.
2. `ɵɵFactoryDeclaration` is now defined as a function. We need this since the provider definition gets passed into the inejctable definition by reference.
3. `ɵɵdefineInjectable`, `ɵɵdefineNgModule` and `ɵɵdefinePipe` now return the typed definition, rather than `unknown`. This aligns with what we do for components and directives.

(cherry picked from commit f9ede9ec98)
2026-03-10 16:58:22 +00:00
Eugene Serkin 106cf4ee90 docs: Update migration docs for vitest
This update fixes minor grammar mistakes

(cherry picked from commit 20496988b1)
2026-03-10 16:23:53 +00:00
Angular Robot 024e307ab0 build: update pnpm to v10.31.0
See associated pull request for more information.
2026-03-09 17:00:10 -07:00
SkyZeroZx 77b035eca6 docs: Adds @see links and update defer reference
(cherry picked from commit 6073493c5b)
2026-03-09 23:59:43 +00:00
Sonu Kapoor 4a9b715b3a test(forms): cover transformedValue without FormField context
Adds a test verifying that `transformedValue` exposes parse errors via
the returned signal's `parseErrors()` property when no FormField
context is present.

This ensures that:
- parse errors are still observable without DI-based field propagation
- the model is not updated when `parse` omits `value`
- valid input clears parse errors and updates the model

This test protects the documented contract that DI-based error
propagation is expected for FormValueControl usage, while standalone
usage relies on explicit consumption of `parseErrors()`.

(cherry picked from commit 71b8159b37)
2026-03-09 23:41:52 +00:00
Jessica Janiuk 420aa725ae docs: Update animation docs for element removal order
This update the docs to be clear that descendent node animations will happen before the parent node is removed.

fixes: #67526
(cherry picked from commit 8c17721333)
2026-03-09 23:40:48 +00:00
Matthieu Riegler c499c578c9 ci: fix exclusion of manual_api_docs
On top of #67253, dev-infra should be required for docs files

(cherry picked from commit bf93ff83d8)
2026-03-09 23:27:47 +00:00
Matthieu Riegler 0588133210 refactor(core): remove private export of getDebugNode
This is a public api

(cherry picked from commit 0afe3c00ae)
2026-03-09 18:21:23 +00:00
Andrew Scott 6416a31c1b refactor(core): update componentDeclaration to include isSignal
This is in DirectiveDeclaration and in the golden files with ComponentDeclaration
so I assume it was an accidental omission.

(cherry picked from commit e97f5139ec)
2026-03-09 18:17:42 +00:00
Andrew Scott b2b903957a release: cut the v21.2.2 release 2026-03-09 11:11:43 -07:00
Matthieu Riegler bd72571583 refactor(devtools): prevent spamming the message bus
This commit adds some state on the bus to prevent sending unecessary messages to the main window.

(cherry picked from commit 263b819a75)
2026-03-09 10:46:08 -07:00
Kam 628f3f99db docs: add Turkish community translation of Angular documentation
add a link to the community-driven Turkish translation of Angular
documentation to make it easier for Turkish-speaking developers to
discover localized docs.

The translation project is maintained by the community and hosted at:
https://github.com/erkamyaman/angular-tr

Providing documentation in developers' native languages helps lower
the barrier to entry and supports wider Angular adoption in the
Turkish-speaking developer community.

(cherry picked from commit 03465360bd)
2026-03-09 10:29:25 -07:00
hawkgs 79ac570c26 refactor(devtools): fix browser-specific styles infra
Fix browser-specific styles infrastructure. PR #62786 cleans up part of the code, but there are still services that attempt to load these stylesheets on `main`.

(cherry picked from commit ed3dc10fea)
2026-03-09 10:22:02 -07:00
Angular Robot 276c12a349 build: update dependency node to v22.22.1
See associated pull request for more information.
2026-03-06 15:32:15 -08:00
Angular Robot 104ac117ea docs: update cross-repo adev docs
Updated Angular adev cross repo docs files.
2026-03-06 15:04:42 -08:00
Angular Robot c2a74548d9 build: update all github actions
See associated pull request for more information.
2026-03-06 14:36:16 -08:00
Kristiyan Kostadinov e7d1d8def3 refactor(core): add tracing for component creation
Adds tracing support for component creations.

(cherry picked from commit c19066c741)
2026-03-06 17:48:27 +00:00
Kristiyan Kostadinov 4a390b6c73 refactor(core): track the tracing service in the LView environment
Adds the `TracingService` to the `LView[ENVIRONMENT]` so we don't have to inject it everywhere.

(cherry picked from commit e433ba9a80)
2026-03-06 17:48:27 +00:00
Anushka Geeta Singh 5a486a65cf docs(router): Add example of relative navigation and multiple array values
Adds an example to docs with relative navigation and multiple values in
the commands array.

(cherry picked from commit 418cb61f2f)
2026-03-06 17:47:44 +00:00
Kristiyan Kostadinov c822bf8e76 fix(compiler-cli): always parenthesize object literals in TCB
This is a follow-up to #67381 which introduced a subtle bug where depending on the type checking configuration, we may put an object literal directly in the TCB body which the TS compiler ends up interpreting as a block. These changes resolve the issue by always wrapping the literal in parentheses.
2026-03-05 14:12:25 -08:00
Kristiyan Kostadinov 31e2c99007 refactor(compiler-cli): resolve presubmit issues
Resolves issues caught during the presubmit.
2026-03-05 14:12:25 -08:00
Kristiyan Kostadinov f8b6bd0cbe refactor(compiler-cli): escape quotes used in string expressions
TypeScript has functionality that automatically escapes quotes in string literals. These changes update the places where we may need to do the same ourselves.
2026-03-05 14:12:25 -08:00
Kristiyan Kostadinov 5349b027b8 refactor(compiler-cli): delete unused utilities
Deletes utilities that we no longer use.
2026-03-05 14:12:25 -08:00
Kristiyan Kostadinov 8f8d67dc57 refactor(compiler-cli): initial decoupling from TypeScript factory APIs
Initial pass to move usages of TS `factory` APIs to the new `TcbExpr`.
2026-03-05 14:12:25 -08:00
Kristiyan Kostadinov 23153a06dd refactor(compiler-cli): introduce new primitive for generating TCB code
Introduces the `TcbExpr` class that will be used to generate TCB code without going through TypeScript's factory APIs.
2026-03-05 14:12:25 -08:00
Alan Agius fd3b4413f3 docs: add a new section on nonces and caching considerations to the security guide.
(cherry picked from commit e5f99edda5)
2026-03-05 18:03:35 +00:00
Leon Senft 670d1660c4 feat(forms): add 'blur' option to debounce rule
Expands the `debounce` rule configuration to accept `'blur'`. When this option
is provided, the rule will delay model synchronization until the field loses
focus (is touched). This introduces a debouncer that defers resolution
until the framework automatically aborts pending debounces upon touch events.

(cherry picked from commit c767d678cf)
2026-03-05 17:55:18 +00:00
Angular Robot 8ca0237d99 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-03-04 16:27:39 -08:00
Joey Perrott 3576019c0f build: no longer require release mode for releases
This commit updates the github.mjs configuration to set requireReleaseModeForRelease to false, removing the requirement to use release mode for releases in the angular/angular repository.

(cherry picked from commit a6941adce8)
2026-03-04 22:48:59 +00:00
Doug Parker 05d022d5e6 fix(compiler-cli): ignore generated ngDevMode signal branch for code coverage
The Angular compiler unconditionally adds a debug name transform for signals
which generates a conditional on `ngDevMode` (e.g., `ngDevMode ? { debugName: "xyz" } : []`).
During testing, `ngDevMode` is true, so the true branch executes but the
false branch is never executed. Consequently, coverage tools report the
false branch as an untested line/branch, preventing 100% test coverage.

This commit adds a synthetic `/* istanbul ignore next */` comment to the
generated false branch so that Istanbul ignores it. We only include the
istanbul comment (instead of additionally including c8) to focus on the
established standard for Angular CLI/Karma coverage while maintaining
compatibility with modern Vitest setups, since @vitest/coverage-v8 now
natively respects the fallback istanbul comment.

Fixes #64583

(cherry picked from commit dc4cf649b6)
2026-03-04 22:42:57 +00:00
Andrew Scott 1df1697c6e fix(compiler): prevent mutation of children array in RecursiveVisitor
RecursiveVisitor.visitIfBlockBranch was permanently mutating the children array by pushing the expressionAlias into it. This change clones the array before pushing to avoid this side effect.

(cherry picked from commit 72a17afaf3)
2026-03-04 22:41:36 +00:00
Andrew Scott 05476ea21b release: bump VSCode extension version to 21.2.2 (#67449) 2026-03-04 12:40:40 -08:00
Jessica Janiuk f38169d0a7 release: cut the v21.2.1 release 2026-03-04 11:34:05 -08:00
Jessica Janiuk 6d7446bc6c refactor(core): account for shadow roots in animations
This adds a util function to get the proper target, properly accounting for shadow roots.

(cherry picked from commit fda08b7a89)
2026-03-04 18:16:43 +00:00
SkyZeroZx c25c84d7dc docs: add customization options for browser URL in routing guide
(cherry picked from commit 9208421290)
2026-03-04 18:10:29 +00:00
Angular Robot f4160c2073 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-03-04 10:01:57 -08:00
Jessica Janiuk 93c6dc6395 Revert "refactor(http): Improves base64 encoding/decoding with feature detection (#67002)"
This reverts commit aafeb1d2bd.

(cherry picked from commit 5338b5912c)
2026-03-04 17:19:14 +00:00
Jessica Janiuk 76431ed3e3 Revert "fix(http): correctly cache blob responses in transfer cache (#67002)"
This reverts commit 1f057afaac.

(cherry picked from commit 7eb33713b9)
2026-03-04 17:19:14 +00:00
Jessica Janiuk b4ec3cc4e4 fix(core): prevent child animation elements from being orphaned
When routing between two different routes, child animations were not finishing, causing elements to be left behind in the dom. The fix ensures the proper fallback is handled to avoid automatically cancelled custom events. This ensures the animation-fallback cancelling the animation actually completes, and ensures the element is removed.

fixes: #67400
(cherry picked from commit 9e64147b73)
2026-03-04 16:21:41 +00:00
Angular Robot 84e79f5add build: update pnpm to v10.30.3
See associated pull request for more information.
2026-03-04 08:03:41 -08:00
SkyZeroZx 2eecf0d9a2 docs: add documentation for dynamic validation schemas
(cherry picked from commit 9fa36d1a71)
2026-03-04 16:01:36 +00:00
Kristiyan Kostadinov da57d1af73 build: use TypeScript 5.9 for patch builds
Partially rolls back to using TypeScript 5.9 for the builds on the patch branch, because we bundle our TypeScript version with the language service which can introduce unexpected breakages for users.

Note that we still allow users to install TypeScript 6.
2026-03-04 07:59:23 -08:00
Georgi Serev 58ea4d2ee8 docs(docs-infra): add Azerbaijani community translation
Add https://angular.az (Azerbaijani) to the community translations section.

(cherry picked from commit e8676b5412)
2026-03-04 15:58:23 +00:00
SkyZeroZx 277ade97ac fix(http): correctly cache blob responses in transfer cache (#67002)
Previously, Blob values were passed to `Uint8Array` this resulted in silently producing an empty array (length = 0) without throwing an error, leading to empty cached data

PR Close #67002
2026-03-04 15:56:59 +00:00
SkyZeroZx aeb9b819d2 refactor(http): Improves base64 encoding/decoding with feature detection (#67002)
Use feature detection for `Uint8Array.prototype.toBase64` and
`Uint8Array.fromBase64`, falling back to the existing implementation
when native support is not available

PR Close #67002
2026-03-04 15:56:59 +00:00
Angular Robot 657d532ad1 build: update dependency preact-render-to-string to v6.6.6
See associated pull request for more information.
2026-03-03 14:48:18 -08:00
Angular Robot 8dafd2413a build: update dependency aspect_rules_jasmine to v2.0.4
See associated pull request for more information.
2026-03-03 14:46:02 -08:00
Angular Robot 796b9a714a build: update cross-repo angular dependencies
See associated pull request for more information.
2026-03-03 14:22:51 -08:00
Krueger01 dd551b1ff3 refactor(compiler-cli): update old angular.io references to angular.dev
Update comment references from the old site angular.io to the new site
angular.dev.

(cherry picked from commit 0e9d58ef09)
2026-03-03 22:22:23 +00:00
Andrew Scott 84adb2fb3b refactor(router): Permit deferring commit of traversal navigations
This updates the state manager to allow intercepting and deferring commits of traversal navigations.
The issues that were encountered in the past appear to be resolved in Chrome.
The behavior of redirect is still undefined in this case, so there is an added TODO.

(cherry picked from commit 778b748694)
2026-03-03 22:09:15 +00:00
Angular Robot 98343ea35e build: update cross-repo angular dependencies to b1b4bd0
See associated pull request for more information.
2026-03-03 10:36:54 -08:00
Angular Robot c4226a10d0 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-03-03 09:54:56 -08:00
Sai Kumar Kola 5e545069ac docs: fix side navigation bar foreground colors in both dark and light modes #67379
(cherry picked from commit 46c996a977)
2026-03-03 17:13:11 +00:00
SkyZeroZx ecf0bb4668 test(http): refactors HTTP client tests to use TestBed and providers
Updates HTTP client, JSONP, and XSRF tests to utilize `TestBed` for setup.

(cherry picked from commit ead6bb1f52)
2026-03-03 17:12:39 +00:00
Ben Hong 0d54c5f0e2 docs: modernize testing service guide
(cherry picked from commit 5955738bc4)
2026-03-03 17:12:03 +00:00
Angular Robot 8f6ca1d9f9 build: lock file maintenance
See associated pull request for more information.
2026-03-03 09:08:50 -08:00
Angular Robot 1f51c61372 build: update cross-repo angular dependencies to 2648276
See associated pull request for more information.
2026-03-02 12:22:46 -08:00
Jessica Janiuk f5e9a862c2 refactor(core): ignore infinite animations in animate api
This ensures that when calculating longest animations, we completely ignore infinite animations. This will prevent mistakes with using the API and hopefully catch any unexpected bugs.

fixes: #67350
(cherry picked from commit 9758ea9ee9)
2026-03-02 19:40:19 +00:00
Angular Robot 0c8a2858b9 build: update cross-repo angular dependencies to v21.2.0
See associated pull request for more information.
2026-03-02 11:32:52 -08:00
Matthieu Riegler a79e8afbad docs: update doc for provideZoneChangeDetection
fixes #67209

(cherry picked from commit a2d5048ff3)
2026-03-02 19:02:34 +00:00
SkyZeroZx 7b7faef739 docs: fixed note on event replay
(cherry picked from commit d793ee0bb7)
2026-03-02 18:59:21 +00:00
Joey Perrott 95afc09206 ci: update the path to the labeling github actions
Update the paths to the labeling github actions after the refactor in dev-infra

(cherry picked from commit 787db90e08)
2026-03-02 18:55:57 +00:00
kirjs 23cb9a4291 refactor(forms): address feedback
Refactor the normalizeFormArgs utility to include the internal FieldAdapter

(cherry picked from commit d2e33e86b9)
2026-03-02 16:46:47 +00:00
kirjs 0a863ba03f refactor(forms): hide adapter in public options
Moves adapter to internal options to prevent exposure but keep compatibility.

(cherry picked from commit 985d828f12)
2026-03-02 16:46:47 +00:00
Jessica Janiuk e923d88398 fix(core): Prevent removal of elements during drag and drop
This addresses a reported issue where elements were being fully removed from the DOM during drag and drop operations.

fixes: #67257
(cherry picked from commit 0b59cba85d)
2026-03-02 16:46:13 +00:00
Angular Robot 150f0b3c30 build: update cross-repo angular dependencies to 90e509f
See associated pull request for more information.
2026-03-02 08:42:55 -08:00
Angular Robot fc5f9d03cf build: update pnpm to v10.30.3
See associated pull request for more information.
2026-03-02 08:37:49 -08:00
Angular Robot 498d9cdda4 build: update cross-repo angular dependencies to 9cc4778
See associated pull request for more information.
2026-02-27 09:11:11 -08:00
Angular Robot fd8d29a157 build: update dependency bazel to v8.6.0
See associated pull request for more information.
2026-02-27 09:10:15 -08:00
Jessica Janiuk e2e9a9a531 fix(core): adds transfer cache to httpResource to fix hydration
This should prevent the microtask problem with hydration and httpResource.

fixes: #62897
(cherry picked from commit 88685cb3b6)
2026-02-27 17:09:26 +00:00
Matthieu Riegler 35fd8b567a build: use different default port for dev-app
Same as ADEV, currently the CLI prompts for another port if 4200 is busy but the toolchain doesn't support prompts.

By defaulting to 4201 we avoid regular CLI apps from preventing to run the dev-app

(cherry picked from commit 9bb4f02bef)
2026-02-27 17:04:19 +00:00
Angular Robot 4e9648d184 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-02-26 14:32:59 -08:00
Angular Robot 54a0541013 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-02-26 13:42:33 -08:00
Joey Perrott 9c0587f755 build: update to latest sha of dev-infra repo
Update to the latest sha of the dev-infra repo

(cherry picked from commit d1ebbbe9bc)
2026-02-26 20:42:17 +00:00
Joey Perrott ac06941bcf build: update to latest sha of dev-infra repo
Update to the latest sha of the dev-infra repo

(cherry picked from commit 385b9485c4)
2026-02-26 20:22:25 +00:00
Joey Perrott 907415c801 ci: set up automated issue labeling
Sets up automated issue labeling action

(cherry picked from commit 37b4d16357)
2026-02-26 20:22:25 +00:00
Angular Robot 225a56677e build: update cross-repo angular dependencies
See associated pull request for more information.
2026-02-26 10:40:54 -08:00
Joey Perrott 944181869d ci: set up linting for upcoming agent skills
Add validation/linting on CI for agent skills files

(cherry picked from commit 078da3cbda)
2026-02-26 18:04:30 +00:00
Joey Perrott 3d3e6cbf20 build: update repository locations for bazel rules
The repositories for `rules_sass`, `rules_angular`, and `rules_browsers`
were recently transferred from devversion to the angular organization.
This commit updates the internal bazel module rules to rely on
the new repository locations.

(cherry picked from commit b4f68acaa1)
2026-02-26 17:57:15 +00:00
Angular Robot 2158bf8bb8 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-02-26 09:44:02 -08:00
Matthieu Riegler 1b46b6db18 docs: add info around route level services
fixes #67313

(cherry picked from commit 95f12c87aa)
2026-02-26 16:20:02 +00:00
Matthieu Riegler f23f3e7f5f docs: add mention of identity tracking for field iterations
related to #66711

(cherry picked from commit 7d92d4454b)
2026-02-26 15:52:58 +00:00
Angular Robot 9414da5b7e build: update all github actions
See associated pull request for more information.
2026-02-26 07:50:54 -08:00
Angular Robot 8078abe893 build: update bazel dependencies
See associated pull request for more information.
2026-02-26 07:50:17 -08:00
Joey Perrott 661e800302 release: bump VSCode extension version to 21.2.1 2026-02-25 15:34:03 -08:00
Angular Robot c960a6e492 build: update cross-repo angular dependencies to v21.2.0
See associated pull request for more information.
2026-02-25 15:11:49 -08:00
Angular Robot 2eeb4b5666 build: update cross-repo angular dependencies to v21.2.0
See associated pull request for more information.
2026-02-25 12:47:33 -08:00
Hien 8f19cf8d84 docs(forms): fix casing of 'FormField' to 'formField' in templates
(cherry picked from commit de0cc817cd)
2026-02-25 20:46:06 +00:00
Jessica Janiuk 50a9ed190d release: cut the v21.2.0 release 2026-02-25 11:29:08 -08:00
Angular Robot e437980659 build: update cross-repo angular dependencies
See associated pull request for more information.

Closes #67275 as a pr takeover
2026-02-25 10:26:00 -08:00
Angular Robot d3ab1ea5f2 build: lock file maintenance
See associated pull request for more information.
2026-02-25 08:52:48 -08:00
Sonu Kapoor fe7b26e403 docs(forms): transformedValue parse error wiring
(cherry picked from commit 547ed65b6f)
2026-02-25 16:13:16 +00:00
SkyZeroZx 70e4c7fbab refactor(common): log a warning when a KeyValuePipe receives a signal
Add signal warning for `KeyValuePipe` and consolidates the `invalidPipeArgumentError` function into a `utils`

(cherry picked from commit 97ed3d9e85)
2026-02-25 16:11:34 +00:00
Angular Robot a25f74238e build: update jasmine dependencies to v6.1.0
See associated pull request for more information.
2026-02-25 07:57:47 -08:00
Angular Robot 61ad0f8b90 build: update pnpm to v10.30.2
See associated pull request for more information.
2026-02-25 07:54:45 -08:00
Matthieu Riegler a86c3214c2 ci: update pullapprove for manual_api docs
The markdown files shouldn't require dev-infra approval.

(cherry picked from commit 0cd00b91ef)
2026-02-24 23:23:34 +00:00
Anushka Geeta Singh 706cfecaee docs: update README to clarify app setup confirmation
Improved wording to clarify how beginners can verify their environment and IDE are correctly set up after running the app.

(cherry picked from commit bdbb7cc7af)
2026-02-24 22:22:32 +00:00
Angular Robot 63cb3f4fa8 build: update bazel dependencies
See associated pull request for more information.
2026-02-24 14:10:29 -08:00
Matthieu Riegler 787cd875b0 refactor(vscode-extension): Add support for exhaustive type check in the syntax
`@default never` will be considered a keyword on its own.

(cherry picked from commit db11e74b3d)
2026-02-24 19:23:50 +00:00
Matthieu Riegler 1a151f2dae docs: add docs for @switch Exhaustive type checking
(cherry picked from commit 9dc3ca44ec)
2026-02-24 18:53:47 +00:00
Munir Issa Said 6b112e6c6c docs: fix roadmap introduction text
(cherry picked from commit 035b151a17)
2026-02-24 18:52:38 +00:00
Doug Parker 7d58b798c6 fix(core): block creation of sensitive URI attributes from ICU messages
Translators are not allowed to write HTML which creates URI attributes. I opted to ban any values going into an attribute at all, to prevent even links to malicious content, rather than just sanitizing URIs.

I also converted this blocklist into an allowlist. Now, we only allowing setting known attributes (while sanitizing URI attributes). This significantly reduces risk of missing a vulnerable attribute and does not require an exhaustive list of all potential attributes.

BREAKING CHANGE: Angular now only applies known attributes from HTML in translated ICU content. Unknown attributes are dropped and not rendered.

(cherry picked from commit 306f367899)
2026-02-24 18:50:41 +00:00
Alan Agius ab1c84eed9 docs: consolidate validation rules for Host and X-Forwarded-Host headers into a single point.
(cherry picked from commit c57ba8c6e6)
2026-02-24 17:24:34 +00:00
Matthieu Riegler 103a44f690 docs: improve responsiveness of the homepage
This fixes some layout issues when on mobile.

(cherry picked from commit 9e0acc5930)
2026-02-24 17:23:43 +00:00
Shuaib Hasan Akib de9092eb23 docs: inline documentation and remove unused references
Replaces referenced doc blocks with inline documentation,
cleans up unused referenced code, and fixes broken list formatting

(cherry picked from commit 8739a22d28)
2026-02-24 17:22:54 +00:00
Jens Kuehlers 0b0b34565a docs: minor roadmap update (Ecosystem/vitest)
(cherry picked from commit dea858979e)
2026-02-24 17:22:13 +00:00
Angular Robot b17a7e062f build: update cross-repo angular dependencies
See associated pull request for more information.
2026-02-24 09:21:23 -08:00
Shuaib Hasan Akib 7c976c3453 docs: remove inconsistent padding in update page
Removes extra padding applied to the update page so its layout
matches the rest of the documentation where no padding exists
between the title and content.

(cherry picked from commit 15b51d0777)
2026-02-24 17:20:55 +00:00
SkyZeroZx eb6bce0ff5 docs: Add image loader config with height parameter
(cherry picked from commit c7dc7054dc)
2026-02-23 23:30:35 +00:00
Suguru Inatomi 30e486a41c fix(docs-infra): use shared heading ID generation logic
This commit extracts the heading ID generation logic into a shared utility
and updates both the route generation script and the markdown pipeline to use it.

This ensures consistency between the generated routes and the rendered
documentation, and fixes an issue where custom heading IDs (`{#id}`)
were ignored during route generation.

Fixes #67200

(cherry picked from commit bedfcb5644)
2026-02-23 23:16:55 +00:00
Matthieu Riegler a73d5435d8 docs: Add a compiler entry to the roadmap
(cherry picked from commit fbabd092f3)
2026-02-23 23:11:30 +00:00
marktechson b9d3fe3b44 docs: update and reformat the roadmap
(cherry picked from commit 53c1a1cee7)
2026-02-23 21:28:43 +00:00
Angular Robot c9ecf9e40b build: update cross-repo angular dependencies to v21.2.0-rc.1
See associated pull request for more information.
2026-02-23 13:17:58 -08:00
Shuaib Hasan Akib 2f213de593 refactor(core): remove outdated TODO comments
Cleans up TODO comments that are no longer planned

Fixes #67191

(cherry picked from commit 5ac1c025f9)
2026-02-23 18:19:57 +00:00
Matthieu Riegler ec32591dd7 docs: mention browser mode debugging
(cherry picked from commit a9ac7f87d7)
2026-02-23 18:19:27 +00:00
Miles Malerba 02b7f5e4f7 ci: remove mmalerba from reviewers
remove mmalerba from reviewers

(cherry picked from commit 0af9e3e9ee)
2026-02-23 18:19:02 +00:00
Angular Robot 31bf694599 build: update pnpm to v10.30.1
See associated pull request for more information.
2026-02-23 09:12:54 -08:00
Miles Malerba bdfb60f3e3 fix(forms): use consistent error format returned from parse
Aligns the errors returned from the `parse` function in
`transformedValue` to use the same convention as the rest of signal
forms (a property called `error` that can contain a single error or list
of errors)

(cherry picked from commit 23fd8fa586)
2026-02-23 17:11:55 +00:00
SkyZeroZx e99b92a1f0 docs: add Route Loading Strategies section and update navigation links
(cherry picked from commit 163dd8ee38)
2026-02-23 17:11:15 +00:00
Alan Agius 5c24de5ee8 docs: move server-side security and host validation documentation from ssr.md to security.md.
(cherry picked from commit 099b4a8bbb)
2026-02-23 17:08:56 +00:00
Alan Agius fad5def327 feat(docs-infra): add hideDollar option to hide the dollar sign prefix in shell code blocks.
The dollar sign is not always required.

(cherry picked from commit efcf76ea61)
2026-02-23 17:08:56 +00:00
Alan Agius de2eab6820 docs: convert SSR guide's important note list to HTML <ul> for proper rendering.
(cherry picked from commit 75fd6c0ff2)
2026-02-23 17:08:56 +00:00
Alan Agius ea1aeb3d45 docs: add documentation for SSR security and host validation, including details on allowedHosts configuration
This document adds more information about `allowedHost` option

(cherry picked from commit 944aefe854)
2026-02-23 17:08:55 +00:00
Jaime Burgos 35f0663ce8 refactor(core): guards stringify calls with ngDevMode
The `stringify` function is only needed for debugging purposes and
should not be called in production mode.

(cherry picked from commit c89d94bd58)
2026-02-20 21:24:31 +00:00
SkyZeroZx 2eeeabb760 fix(common): fix LCP image detection with duplicate URLs
Addresses an issue where the LCP image observer incorrectly identified LCP elements when the same image URL was used multiple times on a page

Fixes #53278

(cherry picked from commit 38749698d0)
2026-02-20 21:23:27 +00:00
Jessica Janiuk 87372893ea ci: update ng-dev config and pullapprove
This updates the pullapprove to remove the auto labeling of requires: TGP and instead moves it to the ng-dev config for pr updates.

(cherry picked from commit d7ddebca90)
2026-02-20 19:02:14 +00:00
cexbrayat 631f60d1f9 fix(forms): preserve parse errors when parse returns value
Fixes #67170 by keeping the errors even a value is returned from the parse function.

(cherry picked from commit fe25c57a5c)
2026-02-20 18:28:57 +00:00
Ben Hong 390efd51e7 docs: add new debugging and troubleshooting di guide
(cherry picked from commit 13e019a1bb)
2026-02-20 18:01:16 +00:00
splincode c2cedd1954 refactor(compiler-cli): improve diagnostic with help link
Add help link to extended template diagnostic messages to provide
users with additional guidance and documentation resources. This
enhancement improves developer experience by making it easier to
understand and resolve complex template issues through direct
access to relevant Angular documentation with detailed examples
and explanations for each diagnostic type.

(cherry picked from commit 66b472e2bc)
2026-02-20 17:40:21 +00:00
Matthieu Riegler 3c4deaa52b refactor(common): log a warning when a JsonPipe receives a signal
The JsonPipe does not unwrap signals and `JSON.stringify` will return `undefined` for signals.
To avoid confusion, we log a warning when a signal is passed to the pipe.

(cherry picked from commit 0d652ba4da)
2026-02-20 17:17:05 +00:00
Matthieu Riegler b5eea233df docs: improve signal demo example
(cherry picked from commit b7cf37d3e1)
2026-02-20 17:15:37 +00:00
SkyZeroZx c0cb6040f8 fix(compiler-cli): detect uninvoked functions in defer trigger expressions
Wrap `@defer` trigger expressions (`when`, `prefetch when`, `hydrate when`)
in a conditional context within the TCB to enable TypeScript's TS2774
diagnostic for detecting functions used without invocation.

Previously, signals and functions passed to `when` triggers without
parentheses would silently evaluate to truthy, causing unexpected behavior.
Now the compiler reports an error when a function is used as a condition
without being called.

(cherry picked from commit f90e5565e0)
2026-02-20 16:57:08 +00:00
Bas ten Feld fb901f7eda docs(docs-infra): fix typo 'requiredSync' instead of 'requireSync'
(cherry picked from commit 57edfaf435)
2026-02-20 16:53:24 +00:00
Matthieu Riegler 17da2c392e docs(docs-infra): remove toString from the API docs
(cherry picked from commit fc8140cff4)
2026-02-20 16:52:01 +00:00
Angular Robot 6cf0100fe4 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-02-19 18:12:11 -08:00
kbrilla d2137928e8 perf(language-service): use lightweight project warmup for Angular analysis
avoid per-file semantic diagnostics work when warming up a newly loaded project.
add ensureProjectAnalyzed() to the language-service API and use it from the server startup path.
implement warmup through public compiler API access with existing perf tracing, and add legacy test coverage for the new warmup flow.

(cherry picked from commit 39f62fa408)
2026-02-20 02:11:06 +00:00
Matthieu Riegler 1d4783c01c refactor(language-service): split tests to reduce risk of timeouts
Because we've had to many timeouts recently

(cherry picked from commit 550b3ba01b)
2026-02-20 02:10:34 +00:00
SkyZeroZx 9ee99c7cc0 docs: update NgTemplateOutlet documentation with injector
(cherry picked from commit 67b22d509a)
2026-02-20 00:06:29 +00:00
Angular Robot 5b8a403220 build: update rules_browsers digest to ceb5275
See associated pull request for more information.

Closes #67141 as a pr takeover
2026-02-19 16:01:02 -08:00
SkyZeroZx 9c8f2dab7d docs: add section on reactive context and async operations
(cherry picked from commit f68078b3e3)
2026-02-19 23:59:59 +00:00
SkyZeroZx 9f1a78ca74 docs(docs-infra): preserve content before docs-card-container in adev
The tokenizer regex pattern `[^<]*` was consuming all non-`<` content
before custom HTML tags, causing lost content.

Changed to `\s*` which only allows leading whitespace, letting marked
properly tokenize preceding content.

(cherry picked from commit bc473cf60f)
2026-02-19 23:48:49 +00:00
Angular Robot d45440eda4 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-02-19 13:31:08 -08:00
Matthieu Riegler eb7c593fda ci: add zone.js to minimumReleaseAgeExclude
This is a package we control the release of.

(cherry picked from commit d501506aa6)
2026-02-19 20:41:24 +00:00
Walter Breakell 48802fd7ed docs: fix grammar, syntax, and broken link in elements guide
(cherry picked from commit 7d21996b1e)
2026-02-19 20:39:04 +00:00
Joey Perrott 5cea3576cf docs: update changelog to note no changes in 21.1.5 release
(cherry picked from commit e8cc910965)
2026-02-19 19:23:48 +00:00
Angular Robot 913bb6ad14 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-02-19 11:22:42 -08:00
Angular Robot 79f1cd462e docs: update cross-repo adev docs
Updated Angular adev cross repo docs files.
2026-02-19 09:03:46 -08:00
SkyZeroZx 4ea3cafdc8 docs(docs-infra): fixed missing gray color palette definitions for light and dark modes
Adds the missing gray color palette definitions for both light and dark modes

(cherry picked from commit 1771d54fb7)
2026-02-19 16:55:02 +00:00
Angular Robot 94b9a3ef70 build: update all github actions
See associated pull request for more information.
2026-02-19 08:52:00 -08:00
Angular Robot c5afb88d9a build: update cross-repo angular dependencies to v21.2.0-rc.0
See associated pull request for more information.
2026-02-19 08:51:02 -08:00
Andrew Scott f58f9c892e release: cut the zone.js-0.16.1 release
(cherry picked from commit ac8b5ff938)
2026-02-18 16:26:41 -08:00
Andrew Scott 7f33cbeb6c release: bump VSCode extension version to 21.2.0 (#67134) 2026-02-18 15:48:34 -08:00
Andrew Scott dd630ef955 build: update version target to prep for 21.2.0 release for vscode extension (#67132)
build: update version target to prep for 21.2.0 release for vscode extension
2026-02-18 15:14:45 -08:00
Matthew Beck 13fd2b1aa3 release: cut the v21.2.0-rc.0 release 2026-02-18 14:38:30 -08:00
4201 changed files with 208382 additions and 286294 deletions
+1 -2
View File
@@ -13,8 +13,7 @@ When reviewing a pull request for the `angular` repository, follow these essenti
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 & Topic Guidelines**: Check if there are specific guidelines for the package or topic being modified in the `reference/` directory (e.g., `reference/router.md` or `reference/object_create_null.md`). Always prioritize these rules for their respective areas.
- **Prototype Collision & `Object.create(null)` PRs**: When reviewing PRs that swap `{}` for `Object.create(null)`, consult `reference/object_create_null.md` for technical evaluation criteria and rules.
- **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.
@@ -1,38 +0,0 @@
# Rules for `Object.create(null)` and Prototype Collision Prevention
This guide outlines the technical rules and evaluation criteria for using `Object.create(null)` versus standard object literals (`{}`) or `Map` in the Angular codebase.
---
## 1. When `Object.create(null)` is Appropriate
Using `Object.create(null)` (or `Map`) is appropriate when **all** of the following conditions are met:
1. The object is used as an **internal key-value lookup map or set**.
2. The keys are **arbitrary or untrusted dynamic strings** (e.g., URL query parameters in `$locationShim`, HTML sanitizer tag sets, or `jsaction` DOM event-type resolvers).
3. Property existence is checked via direct indexing or key checks (e.g., `map[key] !== undefined` or `key in map`), where a key matching an `Object.prototype` member (such as `'toString'`, `'constructor'`, or `'hasOwnProperty'`) causes false positive matches or incorrect behavior.
---
## 2. Handling Public API and Boundary Objects
If an object receives untrusted dynamic keys **and** is exposed to public consumers or third-party code (e.g., `SimpleChanges` in `ngOnChanges`):
- **Do NOT blindly change the object to `Object.create(null)`**: Stripping `Object.prototype` from public objects is a breaking API change. Consumer code calling `.hasOwnProperty()`, `.toString()`, `.valueOf()`, or using string interpolation (`` `${obj}` ``) will fail at runtime (`TypeError: obj.hasOwnProperty is not a function`).
- **Safe Alternatives for Public Objects:**
- **`Object.hasOwn(obj, key)`**: Use `Object.hasOwn` for internal framework property lookups instead of direct index or `in` checks. This prevents prototype collision during internal reads without breaking the object's prototype for consumers.
- **Input Key Sanitization**: Filter or delete dangerous key names (`__proto__`, `constructor`, `prototype`) when populating the object.
- **`Map` or Custom Classes**: For new public APIs requiring key-value stores with dynamic keys, prefer `Map<K, V>` or dedicated classes with explicit `.get()` and `.has()` methods.
- **Deprecation / Breaking Change Process**: If changing a public object's prototype to `null` is unavoidable, it must follow Angular's formal deprecation and major version breaking change process.
---
## 3. When `Object.create(null)` Should NOT Be Used
Do not replace `{}` with `Object.create(null)` in the following scenarios:
1. **Fixed-Shape Structs and DTOs:** Objects with hardcoded static property names (e.g., `let sortedBreakpoints: {breakpoints?: number[]} = {}`). `Object.assign({}, ...)` only copies _own_ enumerable properties, so prototype properties on sources are never copied.
2. **Numeric-Key Maps:** Objects indexed by numbers (e.g., `tasksByHandleId: {[id: number]: Task}`). Numeric keys do not collide with `Object.prototype` string members.
3. **Reference Sentinels:** Objects used purely for reference identity checks (e.g., `const EMPTY_OBJECT = {}` or `const IN_PROGRESS_RESOLUTION = {}`).
4. **Internal Compiler AST and Visitor State:** Temporary objects with internally generated keys where untrusted user input cannot poison key names.
5. **Hot Performance Paths and Size-Critical Bundles:** Standard `{}` literals use V8 fast hidden classes and monomorphic inline caching. `Object.create(null)` forces V8 dictionary mode and increases minified bundle size (e.g., in inline polyfills like `event-dispatch-contract` or SSR hydration bundles).
@@ -5,7 +5,7 @@ description: Explains the mental model and architecture of the code under `packa
# Signal Forms Architecture
The `packages/forms/signals` directory contains the signal-based forms API for Angular.
The `packages/forms/signals` directory contains an experimental, signal-based forms API for Angular.
This system differs significantly from the existing Reactive and Template-driven forms.
## Mental Model
@@ -36,27 +36,27 @@ The central internal class representing a single field in the form graph. It agg
- `structure`: Manages parent/child relationships and signal slicing.
- `validationState`: Computes `valid`, `invalid`, `errors` signals.
- `nodeState`: Tracks `touched`, `dirty`, and derived logical state.
- `nodeState`: Tracks `touched`, `dirty`, `pristine`.
- `metadataState`: Stores metadata like `min`, `max`, `required`.
- `submitState`: Tracks submission status and server errors.
### 2. `FieldValidationState` (`src/field/validation.ts`)
### 2. `ValidationState` (`src/field/validation.ts`)
Implements `ValidationState` and manages the complexity of validation:
Manages the complexity of validation:
- **Synchronous Errors**: Derived from schema rules.
- **Asynchronous Errors**: Handled via signals, including 'pending' states.
- **Tree Errors**: Errors that bubble up or are targeted at specific fields.
- **Submission Errors**: Server-side errors injected imperatively via `submit()`.
### 3. `FormField` Directive (`src/directive/form_field.ts`)
### 3. `FormField` Directive (`src/directive/form_field_directive.ts`)
The bridge between the `FieldNode` and the DOM.
- Selector: `[formField]`
- It supports:
- **Native Elements**: `<input>`, `<select>`, `<textarea>`.
- **Custom Controls**: Components implementing `FormValueControl` or `FormCheckboxControl`.
- **Custom Controls**: Components implementing `FormUiControl` or `FormValueControl`.
- **Legacy Interop**: Components implementing `ControlValueAccessor` (via `InteropNgControl`).
### 4. `Schema` (`src/api/structure.ts` & `src/api/rules`)
@@ -69,7 +69,7 @@ Defines the behavior.
## Data Flow
1. **Read**: `form.field().value()` reads directly from the underlying signal (projected to the specific path).
1. **Read**: `form.field.value()` reads directly from the underlying signal (projected to the specific path).
2. **Write**: Writing to the form (e.g., via UI) updates the underlying signal.
3. **Validation**: A computed effect observes the value signal and runs validators defined in the schema.
@@ -97,7 +97,7 @@ const userForm = form(user, userRules); // OR apply(userForm, userRules)
- `packages/forms/signals/src/api/structure.ts`: Public API entry points (`form`, `apply`).
- `packages/forms/signals/src/api/control.ts`: Interfaces for custom controls (`FormUiControl`).
- `packages/forms/signals/src/field/node.ts`: The `FieldNode` implementation.
- `packages/forms/signals/src/directive/form_field.ts`: The `[formField]` directive.
- `packages/forms/signals/src/directive/form_field_directive.ts`: The `[formField]` directive.
## Supplemental Information
@@ -2,9 +2,9 @@
This document explains how the Signal Forms system hooks into the Angular compiler and runtime to provide seamless type-checking and efficient updates.
## 1. Compiler Integration (Type Checking)
## 1. Compiler-CLI Integration (Type Checking)
The `packages/compiler` package contains specific logic to support `[formField]`. This is primarily handled in `src/typecheck/ops/signal_forms.ts`.
The `packages/compiler-cli` package contains specific logic to support `[formField]`. This is primarily handled in `src/ngtsc/typecheck/src/ops/signal_forms.ts`.
### Key Mechanisms:
@@ -22,7 +22,7 @@ The `packages/compiler` package contains specific logic to support `[formField]`
### Relevant Files:
- `packages/compiler/src/typecheck/ops/signal_forms.ts`: The core Signal Forms type-checking logic, including `TcbNativeFieldOp`.
- `packages/compiler-cli/src/ngtsc/typecheck/src/ops/signal_forms.ts`: The core logic for `TcbNativeFieldOp` and `SignalFormFieldOp`.
## 2. Core Runtime Integration
@@ -30,8 +30,8 @@ The `packages/core` package provides the low-level instructions that power the `
### Key Mechanisms:
- **`ɵngControlCreate` Hook**: The `FormField` directive defines a special method `ɵngControlCreate`, which causes the compiler to install `ɵɵControlFeature`.
- **`ɵɵcontrol` Instructions**: A `formField` binding causes the compiler to emit:
- **`ɵngControlCreate` Hook**: The `FormField` directive defines a special method `ɵngControlCreate`.
- **`ɵɵcontrol` Instructions**: When the compiler sees `ɵngControlCreate`, it emits:
- `ɵɵcontrolCreate`: Called during the creation phase.
- `ɵɵcontrol`: Called during the update phase.
- **`ControlDirectiveHost`**: These instructions provide the directive with a `ControlDirectiveHost`. This is a privileged interface that allows the `FormField` directive to:
@@ -43,4 +43,4 @@ The `packages/core` package provides the low-level instructions that power the `
### Relevant Files:
- `packages/core/src/render3/instructions/control.ts`: Implementation of `ɵɵcontrol` instructions.
- `packages/forms/signals/src/directive/form_field.ts`: The directive that implements the hook.
- `packages/forms/signals/src/directive/form_field_directive.ts`: The directive that implements the hook.
+10
View File
@@ -159,6 +159,16 @@ common --incompatible_allow_tags_propagation
# )
build --nosandbox_default_allow_network
##################################
# Saucelabs tests settings #
# Turn on these settings with #
# --config=saucelabs #
##################################
# For saucelabs tests we don't want to enable flaky test attempts. Karma has its own integrated
# retry mechanism and we do not want to retry unnecessarily if Karma already tried multiple times.
test:saucelabs --flaky_test_attempts=1
################
# Flag Aliases #
################
+1 -1
View File
@@ -1 +1 @@
8.8.0
8.6.0
+3
View File
@@ -0,0 +1,3 @@
Language: JavaScript
BasedOnStyle: Google
ColumnLimit: 100
-2
View File
@@ -43,5 +43,3 @@ c702e8af0b2144d97b93171dc2806ed1a0346762
6d3f5752f204a5a30f3c09fbf6d4a510a4522bcb
3fa7b2b136696464e0a91b5ec25c6adf272d4d6b
ad65f44877f22caadbc8b977b5b720c3c86fcc45
# commit that changed formatting
698b0288bee60b8c5926148b79b5b93f454098db
@@ -11,6 +11,7 @@ esbuild_checked_in(
config = "esbuild.conf.js",
entry_point = ":lib/main.mts",
external = [
"undici",
"pnpapi",
],
metafile = False,
@@ -35,5 +36,7 @@ ts_project(
"//:node_modules/@actions/github",
"//:node_modules/@angular/ng-dev",
"//:node_modules/@types/node",
"//:node_modules/@types/tmp",
"//:node_modules/tmp",
],
)
@@ -1,17 +1,15 @@
import {writeFileSync, mkdtempSync} from 'node:fs';
import {join} from 'node:path';
import {tmpdir} from 'node:os';
import {fileSync} from 'tmp';
import {writeSync} from 'node:fs';
import {getInput, setSecret} from '@actions/core';
let credentialFilePath: undefined | string;
export function getCredentialFilePath(): string {
if (credentialFilePath === undefined) {
const tmpDir = mkdtempSync(join(tmpdir(), 'credential-'));
const filePath = join(tmpDir, 'credential.json');
writeFileSync(filePath, getInput('serviceKey', {required: true}));
setSecret(filePath);
credentialFilePath = filePath;
const tmpFile = fileSync({postfix: '.json'});
writeSync(tmpFile.fd, getInput('serviceKey', {required: true}));
setSecret(tmpFile.name);
credentialFilePath = tmpFile.name;
}
return credentialFilePath;
}
@@ -1,9 +1,6 @@
import {
fetchLongTermSupportBranchesFromNpm,
ActiveReleaseTrains,
AuthenticatedGitClient,
ReleaseConfig,
} from '@angular/ng-dev';
import {fetchLongTermSupportBranchesFromNpm, ActiveReleaseTrains} from '@angular/ng-dev';
import {ReleaseConfig} from '@angular/ng-dev';
import {AuthenticatedGitClient} from '@angular/ng-dev';
export interface Deployment {
branch: string;
File diff suppressed because one or more lines are too long
@@ -5,6 +5,7 @@
"types": ["node"],
"lib": ["es2021"],
"experimentalDecorators": true,
"strict": true,
"skipLibCheck": true,
"noImplicitOverride": true,
"esModuleInterop": true,
@@ -12,4 +13,4 @@
"declaration": true,
"sourceMap": true
}
}
}
@@ -0,0 +1,40 @@
name: 'Saucelabs legacy test job'
description: 'Runs tests against Saucelabs (outside of Bazel)'
runs:
using: 'composite'
steps:
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Saucelabs Variables
uses: angular/dev-infra/github-actions/saucelabs@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Starting Saucelabs tunnel service
shell: bash
run: ./tools/saucelabs/sauce-service.sh run &
# Build test fixtures for a test that rely on Bazel-generated fixtures. Note that disabling
# specific tests which are reliant on such generated fixtures is not an option as SystemJS
# in the Saucelabs legacy job always fetches referenced files, even if the imports would be
# guarded by an check to skip in the Saucelabs legacy job. We should be good running such
# test in all supported browsers on Saucelabs anyway until this job can be removed.
- name: Preparing Bazel-generated fixtures required in legacy tests
shell: bash
run: |
# Locale files are needed for i18n tests running within Saucelabs. These are added
# directly as sources so that the TypeScript compilation of `/packages/tsconfig.json`
# can succeed. Note that the base locale and currencies files are checked-in, so
# we do not need to re-generate those through Bazel.
mkdir -p packages/common/locales/extra
cp dist/bin/packages/common/locales/*.ts packages/common/locales
cp dist/bin/packages/common/locales/extra/*.ts packages/common/locales/extra
- name: Build bundle of tests to run on Saucelabs
shell: bash
run: node tools/legacy-saucelabs/build-saucelabs-test-bundle.mjs
- name: Wait and confirm Saucelabs tunnel has connected
shell: bash
run: ./tools/saucelabs/sauce-service.sh ready-wait
- name: Running tests on Saucelabs.
shell: bash
run: KARMA_WEB_TEST_MODE=SL_REQUIRED pnpm karma start ./karma-js.conf.js --single-run
- name: Stop Saucelabs tunnel service
shell: bash
run: ./tools/saucelabs/sauce-service.sh stop
+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@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
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@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
- 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}}'
+3 -7
View File
@@ -8,7 +8,6 @@
name: Deploying adev preview to Firebase
on:
# zizmor: ignore[dangerous-triggers] - {Trigger is safe as workflow does not checkout untrusted code}
workflow_run:
workflows: ['Build adev for preview deployment']
types: [completed]
@@ -30,14 +29,11 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
token: '${{secrets.GITHUB_TOKEN}}'
persist-credentials: false
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: '.nvmrc'
package-manager-cache: false
- name: Configure Firebase deploy target
working-directory: ./
run: |
@@ -46,7 +42,7 @@ jobs:
npx -y firebase-tools@15.15.0 target:clear --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs
npx -y firebase-tools@15.15.0 target:apply --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs ${{env.PREVIEW_SITE}}
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
github-token: '${{secrets.GITHUB_TOKEN}}'
workflow-artifact-name: 'adev-preview'
@@ -2,7 +2,6 @@ name: DevInfra
on:
push:
# zizmor: ignore[dangerous-triggers] - {Trigger is safe as workflow does not checkout untrusted code}
pull_request_target:
types: [opened, synchronize, reopened, ready_for_review, labeled]
@@ -15,9 +14,9 @@ jobs:
runs-on: ubuntu-latest
if: github.event.repository.fork == false
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: angular/dev-infra/github-actions/branch-manager@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
- uses: angular/dev-infra/github-actions/branch-manager@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+5 -21
View File
@@ -25,10 +25,10 @@ jobs:
token: '${{secrets.BENCHMARK_POST_RESULTS_GITHUB_TOKEN}}'
reactions: 'rocket'
- uses: alessbell/pull-request-comment-branch@ef3408c9757d05f89cb525036383033a313758a0 # v2.1.0
- uses: alessbell/pull-request-comment-branch@653a7d5ca8bd91d3c5cb83286063314d0b063b8e # v1.4.0
id: comment-branch
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# Specify repository as the PR branch might be from a fork.
repository: ${{steps.comment-branch.outputs.head_owner}}/${{steps.comment-branch.outputs.head_repo}}
@@ -36,23 +36,9 @@ jobs:
# that the action was triggered by a team member.
ref: ${{steps.comment-branch.outputs.head_sha}}
# We cannot use `angular/dev-infra/github-actions/npm/checkout-and-setup-node` here
# because it does not support checking out from a fork (as it lacks a `repository` input).
# Thus, we checkout and setup Node/pnpm manually.
- name: Setup Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version-file: '.nvmrc'
- name: Install pnpm
uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
- run: pnpm install --frozen-lockfile
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
- uses: angular/dev-infra/github-actions/bazel/configure-remote@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
- uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
bazelrc: ./.bazelrc.user
@@ -63,10 +49,8 @@ jobs:
COMMENT_BODY: ${{ github.event.comment.body }}
run: pnpm benchmarks prepare-for-github-action "$COMMENT_BODY"
- env:
COMPARE_SHA: ${{steps.info.outputs.compareSha}}
BENCHMARK_TARGET: ${{steps.info.outputs.benchmarkTarget}}
run: pnpm benchmarks run-compare "$COMPARE_SHA" "$BENCHMARK_TARGET"
- run: pnpm benchmarks run-compare ${{steps.info.outputs.compareSha}} "${{steps.info.outputs.benchmarkTarget}}"
id: benchmark
name: Running benchmark
+38 -28
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@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
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
@@ -33,7 +33,7 @@ jobs:
- name: Validate angular robot configuration
run: pnpm ng-dev ngbot verify
- name: Validate agent skills
run: pnpm ng-dev misc validate-skills
run: pnpm ng-dev ai skills validate
- name: Confirm code builds with typescript as expected
run: pnpm check-tooling-setup
@@ -41,13 +41,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
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@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
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@09090944bd8aaa2a517cefb6e38bf1aae336b42b # v7.4.3
uses: cypress-io/github-action@c495c3ddffba403ba11be95fffb67e25203b3799 # v7.1.10
with:
command: pnpm devtools:test:e2e
start: pnpm bazel run //devtools/src:devserver
@@ -69,11 +69,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
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@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
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@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
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@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
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@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
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@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -188,21 +188,31 @@ jobs:
- run: pnpm -C packages/zone.js jest:test
- run: pnpm -C packages/zone.js jest:nodetest
- run: pnpm -C packages/zone.js vitest:test
- run: pnpm -C packages/zone.js vitest-globals:test
- run: pnpm -C packages/zone.js electrontest
- run: pnpm -C packages/zone.js/test/typings test
# saucelabs:
# runs-on: ubuntu-latest
# env:
# SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
# steps:
# - name: Initialize environment
# uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b5a3609f89c06eb4037dce22a93641213a5d1508
# - name: Install node modules
# run: pnpm install --frozen-lockfile
# - uses: ./.github/actions/saucelabs-legacy
adev-deploy:
needs: [adev]
if: needs.adev.result == 'success'
runs-on: ubuntu-latest-8core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Build adev
+1 -1
View File
@@ -24,7 +24,7 @@ jobs:
steps:
- name: Checkout the repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# Setting `persist-credentials: false` prevents the github-action account from being the
# account that is attempted to be used for authentication, instead the remote is set to
+5 -4
View File
@@ -1,7 +1,6 @@
name: DevInfra
on:
# zizmor: ignore[dangerous-triggers] - {Trigger is safe as workflow does not checkout untrusted code}
pull_request_target:
types: [opened, synchronize, reopened]
issues:
@@ -16,7 +15,8 @@ jobs:
if: github.event_name == 'pull_request_target'
runs-on: ubuntu-latest
steps:
- uses: angular/dev-infra/github-actions/labeling/pull-request@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- 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/**/{*,.*}"]}'
@@ -24,14 +24,15 @@ jobs:
if: github.event_name == 'pull_request_target'
runs-on: ubuntu-latest
steps:
- uses: angular/dev-infra/github-actions/post-approval-changes@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- 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@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
- 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 }}
+2 -3
View File
@@ -1,7 +1,6 @@
name: Google Internal Tests Enforcement
on:
# zizmor: ignore[dangerous-triggers] - {Trigger is safe as workflow does not checkout untrusted code}
pull_request_target:
types: [opened, reopened, synchronize]
@@ -14,8 +13,8 @@ jobs:
statuses: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: angular/dev-infra/github-actions/google-internal-tests@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- 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 }}
+32
View File
@@ -0,0 +1,32 @@
name: Manual jobs
on:
workflow_dispatch:
inputs: {}
jobs:
# Bazel saucelabs job resides in `manual.yml` because it's currently unstable, but
# kept as "runnable" for debugging/stabilization effort purposes.
bazel-saucelabs:
runs-on: ubuntu-latest
env:
JOBS: 2
steps:
- name: Initialize environment
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@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Saucelabs Variables
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:
SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
- name: Run all saucelabs bazel tests
run: |
TESTS=$(./node_modules/.bin/bazelisk query --output label '(kind(karma_web_test, ...) intersect attr("tags", "saucelabs", ...)) except attr("tags", "fixme-saucelabs", ...)')
pnpm bazel test --config=saucelabs --jobs=$JOBS ${TESTS}
+2 -4
View File
@@ -1,8 +1,6 @@
name: Merge Ready
on:
# zizmor: ignore[dangerous-triggers] - {Trigger is safe as workflow does not checkout untrusted code}
pull_request_target:
on: pull_request_target
# Declare default permissions as read only.
permissions: {}
@@ -11,6 +9,6 @@ jobs:
status:
runs-on: ubuntu-latest
steps:
- uses: angular/dev-infra/github-actions/unified-status-check@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
- uses: angular/dev-infra/github-actions/unified-status-check@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+5 -10
View File
@@ -7,6 +7,7 @@ on:
permissions:
contents: 'read'
id-token: 'write'
defaults:
run:
@@ -20,7 +21,7 @@ jobs:
workflows: ${{ steps.workflows.outputs.workflows }}
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- id: workflows
@@ -30,17 +31,14 @@ jobs:
timeout-minutes: 30
runs-on: ubuntu-latest
needs: list
permissions:
contents: 'read'
id-token: 'write'
strategy:
matrix:
workflow: ${{ fromJSON(needs.list.outputs.workflows) }}
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
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
@@ -52,7 +50,4 @@ jobs:
project_id: 'internal-200822'
workload_identity_provider: 'projects/823469418460/locations/global/workloadIdentityPools/measurables-tracking/providers/angular'
service_account: 'measures-uploader@internal-200822.iam.gserviceaccount.com'
- run: pnpm ng-dev perf workflows --name "$WORKFLOW" --commit-sha "$COMMIT_SHA"
env:
WORKFLOW: ${{ matrix.workflow }}
COMMIT_SHA: ${{ github.sha }}
- run: pnpm ng-dev perf workflows --name ${{ matrix.workflow }} --commit-sha ${{github.sha}}
+34 -24
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@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
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
@@ -33,13 +33,13 @@ jobs:
- name: Confirm code builds with typescript as expected
run: pnpm check-tooling-setup
- name: Validate agent skills
run: pnpm ng-dev misc validate-skills
run: pnpm ng-dev ai skills validate
- name: Check commit message
run: pnpm ng-dev commit-message validate-range ${{ github.event.pull_request.base.sha }} ${{ github.event.pull_request.head.sha }}
- 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@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
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@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
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@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
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@09090944bd8aaa2a517cefb6e38bf1aae336b42b # v7.4.3
uses: cypress-io/github-action@c495c3ddffba403ba11be95fffb67e25203b3799 # v7.1.10
with:
command: pnpm devtools:test:e2e
start: pnpm bazel run //devtools/src:devserver
@@ -73,11 +73,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
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
@@ -88,7 +88,7 @@ jobs:
ASPECT_RULES_JS_FROZEN_PNPM_LOCK: '1'
- name: Upload GRPC logs (for debugging of RBE issues)
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
path: /tmp/rbe-grpc.log
retention-days: 1
@@ -97,11 +97,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
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@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
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@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
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@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- run: |
@@ -174,6 +174,16 @@ jobs:
- run: pnpm -C packages/zone.js jest:test
- run: pnpm -C packages/zone.js jest:nodetest
- run: pnpm -C packages/zone.js vitest:test
- run: pnpm -C packages/zone.js vitest-globals:test
- run: pnpm -C packages/zone.js electrontest
- run: pnpm -C packages/zone.js/test/typings test
# saucelabs:
# runs-on: ubuntu-latest
# env:
# SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
# steps:
# - name: Initialize environment
# uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b5a3609f89c06eb4037dce22a93641213a5d1508
# - name: Install node modules
# run: pnpm install --frozen-lockfile
# - uses: ./.github/actions/saucelabs-legacy
-24
View File
@@ -1,24 +0,0 @@
name: Publish Release
on:
push:
branches:
- main
- '[0-9]+.[0-9]+.x'
workflow_dispatch: # Allow manual trigger for verification
# Ensure only one release runs at a time PER BRANCH to avoid race conditions.
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
permissions:
contents: read # Required to checkout the repository
id-token: write # Required for NPM provenance in reusable workflow
jobs:
release:
uses: angular/dev-infra/.github/workflows/reusable-release.yml@4a09f9588769980eacb41fcc76e2c76d048eac42 # main
secrets:
wombot-token: ${{ secrets.WOMBOT_TOKEN }}
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+52
View File
@@ -0,0 +1,52 @@
name: OpenSSF Scorecard
on:
branch_protection_rule:
schedule:
- cron: '0 0 * * 0'
push:
branches: [main]
workflow_dispatch:
# Declare default permissions as read only.
permissions:
contents: read
jobs:
analysis:
name: Scorecards analysis
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results
id-token: write
actions: read
contents: read
steps:
- name: 'Checkout code'
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: 'Run analysis'
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
with:
results_file: results.sarif
results_format: sarif
repo_token: ${{ secrets.GITHUB_TOKEN }}
publish_results: true
# Upload the results as artifacts.
- name: 'Upload artifact'
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- name: 'Upload to code-scanning'
uses: github/codeql-action/upload-sarif@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
with:
sarif_file: results.sarif
-1
View File
@@ -10,7 +10,6 @@
"packages/compiler-cli/private/bazel.ts",
"packages/compiler-cli/private/localize.ts",
"packages/compiler-cli/private/tooling.ts",
"packages/compiler-cli/private/hybrid_analysis.ts",
"packages/compiler-cli/private/babel.d.ts",
"packages/compiler-cli/src/bin/**",
"packages/core/schematics/utils/tsurge/helpers/angular_devkit/**",
+2 -16
View File
@@ -7,14 +7,7 @@ export const release = {
publishRegistry: 'https://wombat-dressing-room.appspot.com',
representativeNpmPackage: '@angular/core',
npmPackages: [
{
name: '@angular/animations',
deprecated: {
version: '>=20.2.0-next.3',
message:
'@angular/animations is deprecated. Use `animate.enter` and `animate.leave` instead. For more information see: https://v22.angular.dev/guide/animations.',
},
},
{name: '@angular/animations'},
{name: '@angular/common'},
{name: '@angular/compiler-cli'},
{name: '@angular/compiler'},
@@ -24,14 +17,7 @@ export const release = {
{name: '@angular/language-server'},
{name: '@angular/language-service'},
{name: '@angular/localize'},
{
name: '@angular/platform-browser-dynamic',
deprecated: {
version: '>=20.1.0-next.0',
message:
'@angular/platform-browser-dynamic is deprecated. Use `@angular/platform-browser` instead.',
},
},
{name: '@angular/platform-browser-dynamic'},
{name: '@angular/platform-browser'},
{name: '@angular/platform-server'},
{name: '@angular/router'},
+1
View File
@@ -1,5 +1,6 @@
{
"compilerOptions": {
"strict": true,
"target": "es2020",
"module": "Node16",
"noEmit": true,
+12
View File
@@ -0,0 +1,12 @@
# Yarn Berry doesn't check engines at all, so pnpm shouldn't either.
engine-strict = false
# Disabling pnpm [hoisting](https://pnpm.io/npmrc#hoist) by setting `hoist=false` is recommended on
# projects using rules_js so that pnpm outside of Bazel lays out a node_modules tree similar to what
# rules_js lays out under Bazel (without a hidden node_modules/.pnpm/node_modules)
hoist=false
# Avoid pnpm auto-installing peer dependencies. We want to be explicit about our versions used
# for peer dependencies, avoiding potential mismatches. In addition, it ensures we can continue
# to rely on peer dependency placeholders substituted via Bazel.
auto-install-peers=false
+1 -1
View File
@@ -1 +1 @@
24.21.0
22.22.2
+1 -1
View File
@@ -53,7 +53,7 @@ function readPackage(pkg, context) {
if (pkg.version === '0.0.0-PLACEHOLDER' && localAngularPackages.has(key)) {
pkg.dependencies = {
...pkg.dependencies,
[key]: 'workspace:*',
[key]: 'workspace: *',
};
delete pkg.peerDependencies[key];
-4
View File
@@ -38,10 +38,6 @@ adev/src/content/aria/**/*.json
adev/src/content/cli/**/*.json
adev/src/content/cdk/**/*.json
# Example package.json containing docregion (`// #docregion`) markers, which are
# not valid JSON and cannot be processed by Prettier's json-stringify parser.
adev/src/content/examples/schematics-for-libraries/projects/my-lib/package.json
# Antigravity rules
.agent/rules/agents.md
+35 -24
View File
@@ -36,9 +36,8 @@
version: 3
availability:
users_unavailable:
- devversion
#availability:
# users_unavailable: []
# Meta field that goes unused by PullApprove to allow for defining aliases to be
# used throughout the config.
@@ -104,7 +103,7 @@ groups:
- atscott
- crisbeto
- devversion
- ~thePunderWoman
- thePunderWoman
- kirjs
- JoostK
- ~amishne
@@ -120,7 +119,6 @@ groups:
conditions:
- >
contains_any_globs(files.exclude('packages/core/primitives/*'), [
'.agent/skills/**/{*,.*}',
'contributing-docs/public-api-surface.md',
'dev-app/**/{*,.*}',
'integration/**/{*,.*}',
@@ -138,7 +136,6 @@ groups:
'packages/platform-browser/**/{*,.*}',
'packages/platform-browser-dynamic/**/{*,.*}',
'packages/platform-server/**/{*,.*}',
'packages/private/**/{*,.*}',
'packages/ssr/**/{*,.*}',
'packages/router/**/{*,.*}',
'packages/service-worker/**/{*,.*}',
@@ -153,7 +150,7 @@ groups:
- crisbeto
- devversion
- kirjs
- ~thePunderWoman
- thePunderWoman
- ~pkozlowski-opensource
- JeanMeche
- ~amishne
@@ -208,8 +205,7 @@ groups:
- crisbeto
- devversion
- kirjs
- alan-agius4
- ~thePunderWoman
- thePunderWoman
- ~pkozlowski-opensource
- ~amishne
- ~leonsenft
@@ -260,7 +256,7 @@ groups:
- crisbeto
- kirjs
- JeanMeche
- ~thePunderWoman
- thePunderWoman
- devversion
- josephperrott
- ~pkozlowski-opensource
@@ -286,14 +282,13 @@ groups:
reviewers:
users:
- AleksanderBodurri
- csmick
- ~devversion
- dgp1130
- eduhmc
- hawkgs
- JeanMeche
- hybrist
- ~josephperrott
- ~milomg
- JeanMeche
- milomg
# =========================================================
# Dev-infra
@@ -303,10 +298,9 @@ groups:
conditions:
- >
contains_any_globs(files
.exclude('.pullapprove.yml')
.exclude('.pullapprove.yml')
.exclude('tools/manual_api_docs/blocks/*.md')
.exclude('tools/manual_api_docs/elements/*.md')
.exclude('.agent/**/*.md'),
.exclude('tools/manual_api_docs/elements/*.md'),
[
'{*,.*}',
'.agent/**/{*,.*}',
@@ -323,14 +317,18 @@ groups:
'modules/{*,.*}',
'packages/{*,.*}',
'packages/examples/test-utils/**/{*,.*}',
'packages/private/**/{*,.*}',
'packages/examples/{*,.*}',
'scripts/**/{*,.*}',
'third_party/**/{*,.*}',
'tools/contributing-stats/**/{*,.*}',
'tools/gulp-tasks/**/{*,.*}',
'tools/legacy-saucelabs/**/{*,.*}',
'tools/manual_api_docs/**/{*,.*}',
'tools/pnpm-patches/**/{*,.*}',
'tools/rxjs/**/{*,.*}',
'tools/saucelabs-daemon/**/{*,.*}',
'tools/saucelabs/**/{*,.*}',
'tools/symbol-extractor/**/{*,.*}',
'tools/testing/**/{*,.*}',
'tools/tslint/**/{*,.*}',
@@ -377,7 +375,7 @@ groups:
- ~AndrewKushnir
- ~alxhub
- atscott
- ~thePunderWoman
- thePunderWoman
- ~pkozlowski-opensource
- kirjs
- crisbeto
@@ -413,8 +411,7 @@ groups:
- ~AndrewKushnir
- atscott
- kirjs
- JeanMeche
- ~thePunderWoman
- thePunderWoman
- ~pkozlowski-opensource
- ~amishne
- ~leonsenft
@@ -462,7 +459,7 @@ groups:
- ~AndrewKushnir
- andrewseguin
- dgp1130
- ~thePunderWoman
- thePunderWoman
- josephperrott
# =========================================================
@@ -481,12 +478,26 @@ groups:
users:
- ~pkozlowski-opensource # Pawel Kozlowski
- ~alxhub # Alex Rickabaugh
- ~thePunderWoman # Jessica Janiuk
- thePunderWoman # Jessica Janiuk
- ~AndrewKushnir # Andrew Kushnir
- atscott # Andrew Scott
# External team required reviews
primitives-shared:
<<: *defaults
conditions:
- >
contains_any_globs(files.exclude('packages/core/primitives/**/*spec.ts'), [
'packages/core/primitives/**/{*,.*}',
])
reviewers:
users:
- csmick # Cameron Smick
- mturco # Matt Turco
- iteriani # Thomas Nguyen
- tbondwilkinson # Tom Wilkinson
- rahatarmanahmed # Rahat Ahmed
- e-cline # Ethan Cline
- rockymeza # Rocky Meza
- ENAML # Ethan Cline
####################################################################################
# Override managed result groups
+21 -1
View File
@@ -1,3 +1,4 @@
load("@aspect_rules_js//js:defs.bzl", "js_library")
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")
load("@devinfra//bazel/validation:defs.bzl", "validate_ts_version_matching")
load("@npm//:defs.bzl", "npm_link_all_packages")
@@ -7,6 +8,8 @@ package(default_visibility = ["//visibility:public"])
exports_files([
"LICENSE",
"karma-js.conf.js",
"browser-providers.conf.js",
"package.json",
])
@@ -22,7 +25,7 @@ filegroup(
)
validate_ts_version_matching(
module_bazel = "MODULE.bazel",
module_lock_file = "MODULE.bazel.lock",
package_json = "package.json",
)
@@ -31,6 +34,14 @@ alias(
actual = "//packages:tsconfig-build.json",
)
js_library(
name = "browser-providers",
srcs = [
"browser-providers.conf.d.ts",
"browser-providers.conf.js",
],
)
copy_to_bin(
name = "angularjs_scripts",
srcs = [
@@ -53,6 +64,15 @@ config_setting(
values = {"stamp": "true"},
)
alias(
name = "sauce_connect",
actual = select({
"@devinfra//bazel/constraints:linux_x64": "@sauce_connect_linux_amd64//:bin/sc",
"@devinfra//bazel/constraints:macos_x64": "@sauce_connect_mac//:bin/sc",
"@devinfra//bazel/constraints:macos_arm64": "@sauce_connect_mac//:bin/sc",
}),
)
# When enabled, this flag substitutes dependency versions with snapshot repositories
# for all packages in this repository. Note that this does not apply to peer
# dependencies, as they must be installed directly.
+9 -1580
View File
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -4967,9 +4967,9 @@ To learn about the release highlights and our CLI-powered automated update workf
- **ivy:** i18n - render legacy message ids in `$localize` if requested ([#32937](https://github.com/angular/angular/issues/32937)) ([bcbf3e4](https://github.com/angular/angular/commit/bcbf3e4))
- **language-service:** module definitions on directive hover ([#32763](https://github.com/angular/angular/issues/32763)) ([0d186dd](https://github.com/angular/angular/commit/0d186dd)), closes [#32565](https://github.com/angular/angular/issues/32565)
- **ngcc:** expose `--create-ivy-entry-points` option on ivy-ngcc ([#33049](https://github.com/angular/angular/issues/33049)) ([b2b917d](https://github.com/angular/angular/commit/b2b917d)), closes [/github.com/angular/angular/pull/32999#issuecomment-539937368](https://github.com/angular/angular/pull/32999/issues/issuecomment-539937368)
- update rxjs peerDependencies minimum requirement to 6.5.3 ([#32812](https://github.com/angular/angular/issues/32812)) ([66658c4](https://github.com/angular/angular/commit/66658c4))
- update rxjs peerDependencies minimum requirment to 6.5.3 ([#32812](https://github.com/angular/angular/issues/32812)) ([66658c4](https://github.com/angular/angular/commit/66658c4))
- **ivy:** support ng-add in localize package ([#32791](https://github.com/angular/angular/issues/32791)) ([e41cbfb](https://github.com/angular/angular/commit/e41cbfb))
- **language-service:** allow retrieving synchronized analyzed NgModules ([#32779](https://github.com/angular/angular/issues/32779)) ([98feee7](https://github.com/angular/angular/commit/98feee7))
- **language-service:** allow retreiving synchronized analyzed NgModules ([#32779](https://github.com/angular/angular/issues/32779)) ([98feee7](https://github.com/angular/angular/commit/98feee7))
- **service-worker:** remove deprecated `versionedFiles` option ([#32862](https://github.com/angular/angular/issues/32862)) ([5d5c94d](https://github.com/angular/angular/commit/5d5c94d))
- **language-service:** expose determining the NgModule of a Directive ([#32710](https://github.com/angular/angular/issues/32710)) ([2846505](https://github.com/angular/angular/commit/2846505)), closes [#32565](https://github.com/angular/angular/issues/32565)
- **bazel:** support ts_library targets as entry-points for ng_package ([#32610](https://github.com/angular/angular/issues/32610)) ([217db9b](https://github.com/angular/angular/commit/217db9b))
@@ -5114,7 +5114,7 @@ To learn about the release highlights and our CLI-powered automated update workf
We assume you will fetch rules_nodejs in your WORKSPACE file, and no other dependencies remain here.
Simply remove any calls to this function and the corresponding load statement.
- typescript 3.4 and 3.5 are no longer supported, please update to typescript 3.6
- We no longer directly have a direct dependency on `tslib`. Instead it is now listed a `peerDependency`.
- We no longer directly have a direct depedency on `tslib`. Instead it is now listed a `peerDependency`.
Users not using the CLI will need to manually install `tslib` via;
@@ -6617,7 +6617,7 @@ For example:
- **forms:** properly handle special properties in FormGroup.get ([#22249](https://github.com/angular/angular/issues/22249)) ([dc3e8aa](https://github.com/angular/angular/commit/dc3e8aa)), closes [#17195](https://github.com/angular/angular/issues/17195)
- **platform-server:** avoid clash between server and client style encapsulation attributes ([#24158](https://github.com/angular/angular/issues/24158)) ([e9f2203](https://github.com/angular/angular/commit/e9f2203))
- **platform-server:** avoid dependency cycle when using http interceptor ([#24229](https://github.com/angular/angular/issues/24229)) ([2991b1b](https://github.com/angular/angular/commit/2991b1b)), closes [#23023](https://github.com/angular/angular/issues/23023)
- **platform-server:** don't reflect innerHTML property to attribute ([#24213](https://github.com/angular/angular/issues/24213)) ([c17098d](https://github.com/angular/angular/commit/c17098d)), closes [#19278](https://github.com/angular/angular/issues/19278)
- **platform-server:** don't reflect innerHTML property to attibute ([#24213](https://github.com/angular/angular/issues/24213)) ([c17098d](https://github.com/angular/angular/commit/c17098d)), closes [#19278](https://github.com/angular/angular/issues/19278)
- **platform-server:** provide Domino DOM types globally ([#24116](https://github.com/angular/angular/issues/24116)) ([906b3ec](https://github.com/angular/angular/commit/906b3ec)), closes [#23280](https://github.com/angular/angular/issues/23280) [#23133](https://github.com/angular/angular/issues/23133)
<!-- CHANGELOG SPLIT MARKER -->
@@ -9193,7 +9193,7 @@ Note: 4.0.0-beta.0 release also contains all the changes present in the 2.3.1 re
- **compiler:** support dotted property binding ([8db184d](https://github.com/angular/angular/commit/8db184d)), closes [angular/flex-layout#34](https://github.com/angular/flex-layout/issues/34)
- **compiler:** update to metadata version 3 ([#13464](https://github.com/angular/angular/issues/13464)) ([b9b557c](https://github.com/angular/angular/commit/b9b557c))
- **core:** detectChanges() doesn't work on detached instance ([4d6ac9d](https://github.com/angular/angular/commit/4d6ac9d)), closes [#13426](https://github.com/angular/angular/issues/13426) [#13472](https://github.com/angular/angular/issues/13472)
- **core:** properly destroy embedded Views attached to ApplicationRef ([#13459](https://github.com/angular/angular/issues/13459)) ([d40bbf4](https://github.com/angular/angular/commit/d40bbf4)), closes [#13062](https://github.com/angular/angular/issues/13062)
- **core:** properly destroy embedded Views attatched to ApplicationRef ([#13459](https://github.com/angular/angular/issues/13459)) ([d40bbf4](https://github.com/angular/angular/commit/d40bbf4)), closes [#13062](https://github.com/angular/angular/issues/13062)
- **core:** remove logError from logGroup ([#12925](https://github.com/angular/angular/issues/12925)) ([5fab871](https://github.com/angular/angular/commit/5fab871))
- **forms:** ensure `select[multiple]` retains selections ([b3dcff0](https://github.com/angular/angular/commit/b3dcff0)), closes [#12527](https://github.com/angular/angular/issues/12527) [#12654](https://github.com/angular/angular/issues/12654)
- **forms:** fix Validators.min/maxLength with FormArray ([#13095](https://github.com/angular/angular/issues/13095)) ([7383e4a](https://github.com/angular/angular/commit/7383e4a)), closes [#13089](https://github.com/angular/angular/issues/13089)
+46 -32
View File
@@ -4,42 +4,42 @@ module(
name = "angular",
)
bazel_dep(name = "rules_pkg", version = "1.3.0")
bazel_dep(name = "rules_nodejs", version = "6.7.5")
bazel_dep(name = "aspect_rules_ts", version = "3.10.1")
bazel_dep(name = "aspect_rules_js", version = "3.4.1")
bazel_dep(name = "aspect_rules_esbuild", version = "0.27.0")
bazel_dep(name = "rules_pkg", version = "1.2.0")
bazel_dep(name = "rules_nodejs", version = "6.7.4")
bazel_dep(name = "aspect_rules_ts", version = "3.8.8")
bazel_dep(name = "aspect_rules_js", version = "3.0.3")
bazel_dep(name = "aspect_rules_esbuild", version = "0.25.1")
bazel_dep(name = "aspect_rules_jasmine", version = "2.0.4")
bazel_dep(name = "aspect_rules_rollup", version = "2.0.1")
bazel_dep(name = "bazel_skylib", version = "1.9.2")
bazel_dep(name = "bazel_lib", version = "3.7.2")
bazel_dep(name = "tar.bzl", version = "0.10.8")
bazel_dep(name = "yq.bzl", version = "0.4.0")
bazel_dep(name = "bazel_skylib", version = "1.9.0")
bazel_dep(name = "bazel_lib", version = "3.3.1")
bazel_dep(name = "tar.bzl", version = "0.10.1")
bazel_dep(name = "yq.bzl", version = "0.3.6")
bazel_dep(name = "rules_angular")
git_override(
module_name = "rules_angular",
commit = "5fa856469c642490a6e381a05d7f58f60fad1913",
commit = "b1d295334e70335dab7ac9984a989fae0a9c9dc2",
remote = "https://github.com/angular/rules_angular.git",
)
bazel_dep(name = "devinfra")
git_override(
module_name = "devinfra",
commit = "4a09f9588769980eacb41fcc76e2c76d048eac42",
commit = "48c48fa3848de5bb0ec1c3203558f099765165ab",
remote = "https://github.com/angular/dev-infra.git",
)
bazel_dep(name = "rules_sass")
git_override(
module_name = "rules_sass",
commit = "bf9d6564f57b4df94b4915086ad0e7ad49a95f37",
commit = "a65d937fabd87672f7245aeaa5eb5a0c5a2abead",
remote = "https://github.com/angular/rules_sass.git",
)
bazel_dep(name = "rules_browsers")
git_override(
module_name = "rules_browsers",
commit = "c37398d63f5e990d618d02f4c6fb2fefe40f1a79",
commit = "b03f09ef28a08f8ae07482851cf5ecbf6ac23a2a",
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 = {
"24.21.0-darwin_arm64": ("node-v24.21.0-darwin-arm64.tar.gz", "node-v24.21.0-darwin-arm64", "bed7eea5325e1108f32ce5228ddd6a5f0f08a499ee42aa7442aea583702f6057"),
"24.21.0-darwin_amd64": ("node-v24.21.0-darwin-x64.tar.gz", "node-v24.21.0-darwin-x64", "1462cb3b3046b815cf8ea436d3da450ec1a9f11dac7e5a46b0ada5305d7e8097"),
"24.21.0-linux_arm64": ("node-v24.21.0-linux-arm64.tar.xz", "node-v24.21.0-linux-arm64", "6ad1325edbdb5649c379b75a237147a666c95d4f9ae8d340fef2d1575d289ad2"),
"24.21.0-linux_ppc64le": ("node-v24.21.0-linux-ppc64le.tar.xz", "node-v24.21.0-linux-ppc64le", "1936fd64623a2f98d1fb31b456686d10c30e92639899cfefdefa8835d22adccd"),
"24.21.0-linux_s390x": ("node-v24.21.0-linux-s390x.tar.xz", "node-v24.21.0-linux-s390x", "2ef7e2ecbf7a6c2f3d08d106f6b2f279419c6dac89fe91b8cad193af7890082c"),
"24.21.0-linux_amd64": ("node-v24.21.0-linux-x64.tar.xz", "node-v24.21.0-linux-x64", "fd8e59d5a511510f6a298afb548f18c7d2b1be404d8b4a27d94fbe49f56cb2d6"),
"24.21.0-windows_amd64": ("node-v24.21.0-win-x64.zip", "node-v24.21.0-win-x64", "158f7685b44de51f6c0df1d153526cbcd3e1bc739a8dfc607721cef75de9e541"),
"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 = "24.21.0",
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 = "11.24.0",
pnpm_version_integrity = "sha512-vSfjRel23LC+C3oSKCF7BJqBfiGx81XJDb59xGZxiVqLwebQbCRVRQXqk+oLRfSJon7Bv7yN5qlln8oPFvoAAA==",
pnpm_version = "10.33.2",
pnpm_version_integrity = "sha512-qQ+vb+6rca1sblf5Tg/hoS9dzCLNdU20CulZPraj4LaxLjVAIYuzeuCDQEsfLObbKkEh6XmCm0r/lLmfSdoc+A==",
)
use_repo(pnpm, "pnpm")
@@ -86,7 +86,6 @@ npm.npm_translate_lock(
"//adev:package.json",
"//adev/shared-docs:package.json",
"//adev/shared-docs/pipeline/api-gen:package.json",
"//devtools:package.json",
"//integration:package.json",
"//modules:package.json",
"//packages/animations:package.json",
@@ -110,11 +109,7 @@ npm.npm_translate_lock(
"//tools/bazel/rules_angular_store:package.json",
"//vscode-ng-language-service/integration/project:package.json",
],
lifecycle_hooks = {
# sleep requires node-gyp rebuild but the native module is not needed in Bazel;
# disable the install hook to avoid failing builds when node-gyp is unavailable.
"sleep": [],
},
npmrc = "//:.npmrc",
pnpm_lock = "//:pnpm-lock.yaml",
)
use_repo(npm, "npm")
@@ -122,9 +117,9 @@ 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/6.0.2 | jq -r '.dist.integrity'
ts_integrity = "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
ts_version = "6.0.3",
# 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",
)
use_repo(rules_ts_ext, **{"npm_typescript": "angular_npm_typescript"})
@@ -163,3 +158,22 @@ cldr_xml_data(
"https://github.com/unicode-org/cldr/releases/download/release-%s/core.zip" % CLDR_VERSION: "d5ee2abac64158c04884a722f8ef4830ea22b6c74aac20185be2838db8eda788",
},
)
http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# Fetch sauce connect (tool to open Saucelabs tunnel for Saucelabs browser tests)
http_archive(
name = "sauce_connect_linux_amd64",
build_file_content = """exports_files(["bin/sc"], visibility = ["//visibility:public"])""",
sha256 = "26b9c3630f441b47854b6032f7eca6f1d88d3f62e50ee44c27015d71a5155c36",
strip_prefix = "sc-4.8.2-linux",
url = "https://saucelabs.com/downloads/sc-4.8.2-linux.tar.gz",
)
http_archive(
name = "sauce_connect_mac",
build_file_content = """exports_files(["bin/sc"], visibility = ["//visibility:public"])""",
sha256 = "28277ce81ef9ab84f5b87b526258920a8ead44789a5034346e872629bbf38089",
strip_prefix = "sc-4.8.2-osx",
url = "https://saucelabs.com/downloads/sc-4.8.2-osx.zip",
)
+3457 -328
View File
File diff suppressed because it is too large Load Diff
+1 -3
View File
@@ -16,7 +16,6 @@ exports_files([
APPLICATION_FILES = [
"//adev/src/assets/images",
"//adev/src/assets/images/v21-event:v21-event-images",
"//adev/src/assets/images/v22-event:v22-event-images",
"//adev/src/assets/others",
"//adev/src/assets/previews",
"//adev/src/assets:tutorials",
@@ -71,6 +70,7 @@ APPLICATION_DEPS = [
":node_modules/fflate",
":node_modules/marked",
":node_modules/ngx-progressbar",
":node_modules/open-in-idx",
":node_modules/tailwindcss",
":node_modules/typescript",
":node_modules/@types/dom-navigation",
@@ -84,7 +84,6 @@ APPLICATION_DEPS = [
":node_modules/@algolia/client-common",
":node_modules/@algolia/requester-browser-xhr",
":node_modules/@algolia/requester-node-http",
":node_modules/beasties",
":node_modules/@lezer/css",
":node_modules/@lezer/html",
":node_modules/@lezer/lr",
@@ -98,7 +97,6 @@ APPLICATION_DEPS = [
":node_modules/@shikijs/langs",
":node_modules/@shikijs/engine-oniguruma",
":node_modules/@shikijs/core",
":node_modules/@shikijs/primitive",
":node_modules/@shikijs/engine-javascript",
":node_modules/@shikijs/types",
":node_modules/@shikijs/vscode-textmate",
+9 -6
View File
@@ -7,7 +7,9 @@
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
"style": "scss",
"standalone": true,
"changeDetection": "OnPush"
}
},
"root": ".",
@@ -31,8 +33,7 @@
"src/robots.txt",
"src/llms.txt",
"src/assets",
"src/assets/images/v21-event",
"src/assets/images/v22-event"
"src/assets/images/v21-event"
],
"styles": ["@angular/docs/styles/global-styles.scss", "./src/local-styles.scss"],
"scripts": [],
@@ -70,16 +71,18 @@
"buildTarget": "web-ui:build",
"headers": {
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "credentialless"
"Cross-Origin-Embedder-Policy": "require-corp"
}
}
},
"test": {
"builder": "@angular/build:unit-test",
"options": {
"options": {
"runner": "karma",
"browsers": ["ChromeHeadlessNoSandbox"],
"include": ["src/app/**/*.spec.ts"]
"include": [
"src/app/**/*.spec.ts"
]
}
}
}
+2 -2
View File
@@ -49,7 +49,7 @@
"headers": [
{
"key": "Cross-Origin-Embedder-Policy",
"value": "credentialless"
"value": "require-corp"
}
]
},
@@ -71,7 +71,7 @@
},
{
"key": "Cross-Origin-Embedder-Policy",
"value": "credentialless"
"value": "require-corp"
}
]
}
+54 -55
View File
@@ -1,105 +1,104 @@
{
"dependencies": {
"@algolia/client-common": "5.57.0",
"@algolia/client-search": "5.57.0",
"@algolia/requester-browser-xhr": "5.57.0",
"@algolia/requester-node-http": "5.57.0",
"@algolia/client-common": "5.48.0",
"@algolia/client-search": "5.48.0",
"@algolia/requester-browser-xhr": "5.48.0",
"@algolia/requester-node-http": "5.48.0",
"@angular/animations": "workspace:*",
"@angular/aria": "22.2.0-next.5",
"@angular/build": "22.2.0-next.7",
"@angular/cdk": "22.2.0-next.5",
"@angular/cli": "22.2.0-next.7",
"@angular/aria": "21.2.9",
"@angular/build": "21.2.9",
"@angular/cdk": "21.2.9",
"@angular/cli": "21.2.9",
"@angular/common": "workspace:*",
"@angular/compiler": "workspace:*",
"@angular/compiler-cli": "workspace:*",
"@angular/core": "workspace:*",
"@angular/docs": "workspace:*",
"@angular/forms": "workspace:*",
"@angular/material": "22.2.0-next.5",
"@angular/material": "21.2.9",
"@angular/platform-browser": "workspace:*",
"@angular/platform-server": "workspace:*",
"@angular/router": "workspace:*",
"@angular/ssr": "22.2.0-next.7",
"@codemirror/autocomplete": "6.20.3",
"@codemirror/commands": "6.11.0",
"@angular/ssr": "21.2.9",
"@codemirror/autocomplete": "6.20.0",
"@codemirror/commands": "6.10.1",
"@codemirror/lang-angular": "0.1.4",
"@codemirror/lang-css": "6.3.1",
"@codemirror/lang-html": "6.4.12",
"@codemirror/lang-javascript": "6.2.5",
"@codemirror/lang-html": "6.4.11",
"@codemirror/lang-javascript": "6.2.4",
"@codemirror/lang-sass": "6.0.2",
"@codemirror/language": "6.12.4",
"@codemirror/lint": "6.9.7",
"@codemirror/search": "6.7.1",
"@codemirror/state": "6.7.1",
"@codemirror/view": "6.43.9",
"@lezer/common": "1.5.2",
"@lezer/css": "1.3.6",
"@codemirror/language": "6.12.1",
"@codemirror/lint": "6.9.3",
"@codemirror/search": "6.6.0",
"@codemirror/state": "6.5.4",
"@codemirror/view": "6.39.12",
"@lezer/common": "1.5.1",
"@lezer/css": "1.3.0",
"@lezer/highlight": "1.2.3",
"@lezer/html": "1.3.13",
"@lezer/javascript": "1.5.4",
"@lezer/lr": "1.4.10",
"@lezer/lr": "1.4.8",
"@lezer/sass": "1.1.0",
"@marijn/find-cluster-break": "1.0.4",
"@shikijs/core": "^4.0.0",
"@shikijs/engine-javascript": "^4.0.0",
"@shikijs/engine-oniguruma": "^4.0.0",
"@shikijs/langs": "^4.0.0",
"@shikijs/primitive": "^4.0.0",
"@shikijs/themes": "^4.0.0",
"@shikijs/types": "^4.0.0",
"@marijn/find-cluster-break": "1.0.2",
"@shikijs/core": "^3.21.0",
"@shikijs/engine-javascript": "^3.21.0",
"@shikijs/engine-oniguruma": "^3.21.0",
"@shikijs/langs": "^3.21.0",
"@shikijs/themes": "^3.21.0",
"@shikijs/types": "^3.21.0",
"@shikijs/vscode-textmate": "^10.0.2",
"@stackblitz/sdk": "1.11.1",
"@types/dom-navigation": "1.0.7",
"@stackblitz/sdk": "1.11.0",
"@types/dom-navigation": "1.0.6",
"@types/jasmine": "6.0.0",
"@types/jsdom": "30.0.0",
"@types/node": "24.13.3",
"@typescript/vfs": "1.6.4",
"@webcontainer/api": "1.6.4",
"@types/jsdom": "27.0.0",
"@types/node": "24.10.11",
"@typescript/vfs": "1.6.2",
"@webcontainer/api": "1.6.1",
"@xterm/addon-fit": "0.11.0",
"@xterm/xterm": "6.0.0",
"algoliasearch": "5.57.0",
"algoliasearch": "5.48.0",
"angular-split": "20.0.0",
"beasties": "0.5.4",
"ccount": "^2.0.1",
"character-entities-html4": "^2.1.0",
"character-entities-legacy": "^3.0.0",
"comma-separated-tokens": "^2.0.3",
"crelt": "1.0.7",
"diff": "9.0.0",
"crelt": "1.0.6",
"diff": "8.0.3",
"emoji-regex": "10.6.0",
"fflate": "0.8.3",
"fflate": "0.8.2",
"hast-util-to-html": "^9.0.5",
"hast-util-whitespace": "^3.0.0",
"html-void-elements": "^3.0.0",
"jasmine-core": "6.3.0",
"jsdom": "30.0.1",
"jsdom": "28.0.0",
"jasmine-core": "6.1.0",
"karma-chrome-launcher": "3.2.0",
"karma-coverage": "2.2.1",
"karma-jasmine": "5.1.0",
"karma-jasmine-html-reporter": "2.3.0",
"marked": "18.0.11",
"mermaid": "11.17.2",
"karma-jasmine-html-reporter": "2.2.0",
"marked": "17.0.1",
"mermaid": "11.12.2",
"ngx-progressbar": "14.0.0",
"playwright-core": "1.62.1",
"preact": "10.29.8",
"preact-render-to-string": "6.7.0",
"prettier": "3.9.6",
"open-in-idx": "0.1.1",
"playwright-core": "1.58.1",
"preact": "10.28.3",
"preact-render-to-string": "6.6.6",
"prettier": "3.8.1",
"property-information": "^7.1.0",
"rxjs": "7.8.2",
"shiki": "4.4.3",
"shiki": "3.22.0",
"space-separated-tokens": "^2.0.2",
"stringify-entities": "^4.0.4",
"style-mod": "4.1.3",
"tinyglobby": "0.2.17",
"tinyglobby": "0.2.15",
"tslib": "2.8.1",
"typescript": "6.0.3",
"typescript": "5.9.3",
"w3c-keyname": "2.2.8",
"zwitch": "^2.0.4"
},
"devDependencies": {
"autoprefixer": "10.5.4",
"autoprefixer": "10.4.24",
"karma": "~6.4.4",
"postcss": "8.5.26",
"postcss": "8.5.6",
"tailwindcss": "3.4.19"
}
}
-7
View File
@@ -51,11 +51,4 @@ js_run_binary(
outs = ["defined-routes.json"],
chdir = package_name(),
tool = ":generate_route",
# Public so the api-gen rendering pipeline (used from //packages/...) can validate guide
# links against the same set of routes defined for the live site.
visibility = [
"//adev:__subpackages__",
"//packages:__subpackages__",
"//tools:__subpackages__",
],
)
+22 -21
View File
@@ -7,7 +7,7 @@
*/
import {ALL_ITEMS} from '../../src/app/routing/navigation-entries/index.js';
import {extractHeadingIds, findDuplicateIds} from '../../shared-docs/pipeline/shared/heading.mjs';
import {getIdFromHeading} from '../../shared-docs/pipeline/shared/heading.mjs';
import {NavigationItem} from '@angular/docs';
import {writeFileSync, readFileSync} from 'fs';
import {join, resolve} from 'path';
@@ -20,7 +20,7 @@ const contentRoot = resolve(process.cwd(), '../../src/content');
* in a JSON file. This file then used by other bazel targets to know which routes are valid.
*/
function extractRoutes(items: NavigationItem[], duplicatesByPage: Map<string, string[]>): string[] {
function extractRoutes(items: NavigationItem[]): string[] {
const routes: string[] = [];
for (const item of items) {
if (item.path && !item.path.startsWith('http')) {
@@ -29,11 +29,7 @@ function extractRoutes(items: NavigationItem[], duplicatesByPage: Map<string, st
const content = readFileSync(join(contentRoot, `${item.contentPath}.md`), {
encoding: 'utf-8',
});
const headings = extractHeadingIds(content);
const duplicates = findDuplicateIds(headings);
if (duplicates.length > 0) {
duplicatesByPage.set(`${item.contentPath}.md`, duplicates);
}
const headings = extractHeadings(content);
routes.push(
...headings.map(
(heading) => `${item.path}#${heading.toLowerCase().replace(/\s+/g, '-')}`,
@@ -42,28 +38,33 @@ function extractRoutes(items: NavigationItem[], duplicatesByPage: Map<string, st
}
}
if (item.children) {
routes.push(...extractRoutes(item.children, duplicatesByPage));
routes.push(...extractRoutes(item.children));
}
}
return routes;
}
function extractHeadings(content: string): string[] {
const headings = content
.split('\n')
// Top level heading (H1) are used for the page title only
// and yes, headings can have leading spaces
.filter((line) => line.trim().startsWith('##'))
.map((line) => line.replace(/^#+\s*/, '').trim());
const stepRegex = /<docs-step[^>]*title="([^"]*)"/g;
let match;
while ((match = stepRegex.exec(content)) !== null) {
headings.push(match[1]);
}
return headings.map((heading: string) => getIdFromHeading(heading));
}
function main() {
const allRoutes: string[] = [];
const duplicatesByPage = new Map<string, string[]>();
allRoutes.push(...extractRoutes(ALL_ITEMS, duplicatesByPage));
if (duplicatesByPage.size > 0) {
const details = Array.from(duplicatesByPage)
.map(([page, ids]) => ` ${page}: ${ids.map((id) => `#${id}`).join(', ')}`)
.join('\n');
throw new Error(
`Headings must produce a unique anchor id within a page, otherwise every link to the ` +
`anchor resolves to the first heading that claims it. Give the later heading its own ` +
`id with the \`{#custom-id}\` syntax.\n${details}`,
);
}
allRoutes.push(...extractRoutes(ALL_ITEMS));
const uniqueRoutes = Array.from(new Set(allRoutes.filter((r) => !!r)));
+1
View File
@@ -2,6 +2,7 @@
"compileOnSave": false,
"compilerOptions": {
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
@@ -10,8 +10,6 @@ import {get} from 'node:https';
import {posix} from 'node:path';
const GITHUB_API = 'https://api.github.com/repos/';
const SHA_REGEX = /^[0-9a-f]{40}$/i;
const BRANCH_REGEX = /^(?!.*\.\.)[a-zA-Z0-9/_.-]+$/;
export class GithubClient {
#token;
@@ -32,12 +30,6 @@ export class GithubClient {
* @returns Promise<string[]>
*/
async getAffectedFiles(baseSha, headSha) {
if (!SHA_REGEX.test(baseSha)) {
throw new Error(`Invalid base SHA: ${baseSha}`);
}
if (!SHA_REGEX.test(headSha)) {
throw new Error(`Invalid head SHA: ${headSha}`);
}
const {files} = JSON.parse(await this.#httpGet(`${this.#api}/compare/${baseSha}...${headSha}`));
return files.map((f) => f.filename);
}
@@ -49,9 +41,6 @@ export class GithubClient {
* @returns Promise<string>
*/
async getShaForBranch(branch) {
if (!BRANCH_REGEX.test(branch)) {
throw new Error(`Invalid branch name: ${branch}`);
}
const sha = await this.#httpGet(`${this.#api}/commits/${branch}`, {
headers: {Accept: 'application/vnd.github.VERSION.sha'},
});
@@ -60,7 +49,7 @@ export class GithubClient {
throw new Error(`Unable to extract the SHA for '${branch}'.`);
}
return sha.trim();
return sha;
}
#httpGet(url, options = {}) {
@@ -8,7 +8,7 @@
//tslint:disable:no-console
import assert from 'node:assert';
import {execFileSync} from 'node:child_process';
import {execSync} from 'node:child_process';
import {existsSync, constants as fsConstants} from 'node:fs';
import {
copyFile,
@@ -41,16 +41,6 @@ export async function updateAssets({repo, assetsPath, destPath}) {
await readFile(buildInfoPath, 'utf-8'),
);
const shaRegex = /^[0-9a-f]{40}$/i;
const branchRegex = /^(?!.*\.\.)[a-zA-Z0-9/_.-]+$/;
if (!shaRegex.test(storedSha)) {
throw new Error(`Invalid SHA in build info: ${storedSha}`);
}
if (!branchRegex.test(storedBranch)) {
throw new Error(`Invalid branch name in build info: ${storedBranch}`);
}
assert(process.env.ANGULAR_READONLY_GITHUB_TOKEN);
const githubApi = new GithubClient(
repo,
@@ -73,10 +63,6 @@ export async function updateAssets({repo, assetsPath, destPath}) {
downstreamBranch = storedBranch;
}
if (!shaRegex.test(latestSha)) {
throw new Error(`Invalid SHA resolved: ${latestSha}`);
}
console.log(`Comparing ${storedSha}...${latestSha}.`);
const affectedFiles = await githubApi.getAffectedFiles(storedSha, latestSha);
const changedFiles = affectedFiles.filter((file) => file.startsWith(`${assetsPath}/`));
@@ -92,18 +78,14 @@ export async function updateAssets({repo, assetsPath, destPath}) {
try {
const execOptions = {cwd: temporaryDir, stdio: 'inherit'};
execFileSync('git', ['init'], execOptions);
execFileSync(
'git',
['remote', 'add', 'origin', `https://github.com/${repo}.git`],
execOptions,
);
execSync('git init', execOptions);
execSync(`git remote add origin https://github.com/${repo}.git`, execOptions);
// fetch a commit
execFileSync('git', ['fetch', 'origin', latestSha], execOptions);
execSync(`git fetch origin ${latestSha}`, execOptions);
// reset this repository's main branch to the commit of interest
execFileSync('git', ['reset', '--hard', 'FETCH_HEAD'], execOptions);
execSync('git reset --hard FETCH_HEAD', execOptions);
// get sha when files where changed
shaWhenFilesChanged = execFileSync('git', ['rev-list', '-1', latestSha, `${assetsPath}/`], {
shaWhenFilesChanged = execSync(`git rev-list -1 ${latestSha} "${assetsPath}/"`, {
encoding: 'utf8',
cwd: temporaryDir,
stdio: ['ignore', 'pipe', 'ignore'],
@@ -6,10 +6,11 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component} from '@angular/core';
import {ChangeDetectionStrategy, Component} from '@angular/core';
@Component({
selector: 'docs-algolia-icon',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './algolia-icon.component.html',
})
export class AlgoliaIcon {}
@@ -6,16 +6,17 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, computed, inject} from '@angular/core';
import {RouterLink} from '@angular/router';
import {NavigationItem} from '../../interfaces/index';
import {ChangeDetectionStrategy, Component, inject, computed} from '@angular/core';
import {NavigationState} from '../../services/index';
import {NavigationItem} from '../../interfaces/index';
import {RouterLink} from '@angular/router';
@Component({
selector: 'docs-breadcrumb',
imports: [RouterLink],
templateUrl: './breadcrumb.component.html',
styleUrls: ['./breadcrumb.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class Breadcrumb {
private readonly navigationState = inject(NavigationState);
@@ -1,16 +1,8 @@
@if (!hasAccepted()) {
<div class="docs-cookies-popup docs-invert-mode">
<div class="docs-cookies-popup-header">
<img
class="docs-cookies-popup-angie"
src="assets/images/angie/greeting.svg"
alt=""
aria-hidden="true"
/>
<p>This site uses cookies from Google to deliver its services and to analyze traffic.</p>
</div>
<p>This site uses cookies from Google to deliver its services and to analyze traffic.</p>
<div class="docs-cookies-popup-actions">
<div>
<a
href="https://policies.google.com/technologies/cookies"
target="_blank"
@@ -15,34 +15,15 @@
border-radius: 0.25rem;
font-size: 0.875rem;
max-width: 265px;
transition:
background-color 0.3s ease,
border-color 0.3s ease,
color 0.3s ease;
transition: background-color 0.3s ease, border-color 0.3s ease, color 0.3s ease;
box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
.docs-cookies-popup-header {
display: flex;
gap: 0.75rem;
align-items: center;
}
.docs-cookies-popup-angie {
flex-shrink: 0;
width: 52px;
height: auto;
}
.docs-cookies-popup-actions {
> div {
display: flex;
gap: 0.5rem;
align-items: center;
width: 100%;
margin-block-start: 1rem;
.docs-primary-btn {
flex: 1;
}
}
p {
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, inject, signal} from '@angular/core';
import {ChangeDetectionStrategy, Component, inject, signal} from '@angular/core';
import {LOCAL_STORAGE} from '../../providers/index';
import {setCookieConsent} from '../../utils';
@@ -21,6 +21,7 @@ export const STORAGE_KEY = 'docs-accepts-cookies';
selector: 'docs-cookie-popup',
templateUrl: './cookie-popup.component.html',
styleUrls: ['./cookie-popup.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CookiePopup {
private readonly localStorage = inject(LOCAL_STORAGE);
@@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {ChangeDetectionStrategy, Component, inject, input, signal} from '@angular/core';
import {Clipboard} from '@angular/cdk/clipboard';
import {Component, inject, input, signal} from '@angular/core';
import {MatTooltip} from '@angular/material/tooltip';
import {IconComponent} from '../icon/icon.component';
@@ -15,7 +15,7 @@ export const CONFIRMATION_DISPLAY_TIME_MS = 1000;
@Component({
selector: 'docs-copy-link-button',
template: `<docs-icon></docs-icon>`,
template: `<docs-icon>{{ showCopySuccess() ? 'check' : 'link' }}</docs-icon>`,
styles: `
:host {
cursor: pointer;
@@ -31,13 +31,6 @@ export const CONFIRMATION_DISPLAY_TIME_MS = 1000;
:host(.docs-copy-link-success) {
color: var(--bright-blue);
}
docs-icon::before {
content: '\\e250'; /* codepoint for "link" */
font-family: 'Material Symbols Outlined';
}
:host(.docs-copy-link-success) docs-icon::before {
content: '\\e668'; /* codepoint for "check" */
}
`,
hostDirectives: [
{
@@ -52,6 +45,7 @@ export const CONFIRMATION_DISPLAY_TIME_MS = 1000;
'[class.docs-copy-link-success]': 'showCopySuccess()',
},
imports: [IconComponent],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CopyLinkButton {
readonly href = input.required<string>();
@@ -8,13 +8,13 @@
import {ComponentFixture, TestBed} from '@angular/core/testing';
import {Clipboard} from '@angular/cdk/clipboard';
import {Component, signal} from '@angular/core';
import {By} from '@angular/platform-browser';
import {
CONFIRMATION_DISPLAY_TIME_MS,
CopySourceCodeButton,
} from './copy-source-code-button.component';
import {ChangeDetectionStrategy, Component, signal} from '@angular/core';
import {By} from '@angular/platform-browser';
import {Clipboard} from '@angular/cdk/clipboard';
const SUCCESSFULLY_COPY_CLASS_NAME = 'docs-copy-source-code-button-success';
const FAILED_COPY_CLASS_NAME = 'docs-copy-source-code-button-failed';
@@ -99,6 +99,7 @@ describe('CopySourceCodeButton', () => {
<button docs-copy-source-code></button>
`,
imports: [CopySourceCodeButton],
changeDetection: ChangeDetectionStrategy.OnPush,
})
class CodeSnippetWrapper {
code = signal('');
@@ -6,8 +6,8 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Clipboard} from '@angular/cdk/clipboard';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
@@ -15,6 +15,7 @@ import {
inject,
signal,
} from '@angular/core';
import {Clipboard} from '@angular/cdk/clipboard';
import {IconComponent} from '../icon/icon.component';
export const REMOVED_LINE_CLASS_NAME = '.line.remove';
@@ -32,6 +33,7 @@ export const CONFIRMATION_DISPLAY_TIME_MS = 2000;
'[class.docs-copy-source-code-button-success]': 'showCopySuccess()',
'[class.docs-copy-source-code-button-failed]': 'showCopyFailure()',
},
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CopySourceCodeButton {
private readonly changeDetector = inject(ChangeDetectorRef);
@@ -7,10 +7,18 @@
*/
import {DOCUMENT} from '@angular/common';
import {Component, afterNextRender, computed, inject, signal} from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
afterNextRender,
computed,
inject,
signal,
} from '@angular/core';
@Component({
selector: 'docs-icon',
changeDetection: ChangeDetectionStrategy.OnPush,
host: {
'class': 'material-symbols-outlined',
'[style.font-size.px]': 'fontSize()',
@@ -24,7 +24,6 @@ ng_project(
"//adev/shared-docs/interfaces",
"//adev/shared-docs/pipes",
"//adev/shared-docs/services",
"//adev/shared-docs/utils",
],
)
@@ -9,17 +9,11 @@
>
@for (itemGroup of groupItems(navigationItems, preserveOtherCategoryOrder); track $index) {
@let groupLabel = itemGroup[0];
@let group = itemGroup[1];
@let items = group.items;
@let items = itemGroup[1];
@let firstItem = items[0];
@if (groupLabel && collapsableLevel() !== firstItem.level && items.length > 1) {
<li class="docs-navigation-group">
<span>{{ groupLabel }}</span>
<ng-container
*ngTemplateOutlet="itemStatus; context: {$implicit: {status: group.status}}"
/>
</li>
<li class="docs-navigation-group">{{ groupLabel }}</li>
}
@for (item of items; track $index) {
<li
@@ -33,7 +27,7 @@
[href]="item.path"
target="_blank"
[matTooltip]="item.label"
[matTooltipDisabled]="isMobile || itemLabel.length < labelTruncationThreshold"
[matTooltipDisabled]="itemLabel.length < 27"
matTooltipPosition="after"
[attr.aria-label]="item.label"
[matTooltipClass]="'API-tooltip'"
@@ -61,7 +55,7 @@
}"
(click)="emitClickOnLink(item)"
[matTooltip]="item.label"
[matTooltipDisabled]="isMobile || itemLabel.length < labelTruncationThreshold"
[matTooltipDisabled]="itemLabel.length < 27"
matTooltipPosition="after"
[attr.aria-label]="item.label"
[matTooltipClass]="'API-tooltip'"
@@ -82,7 +76,7 @@
<div
class="docs-secondary-nav-header"
[matTooltip]="item.label"
[matTooltipDisabled]="isMobile || itemLabel.length < labelTruncationThreshold"
[matTooltipDisabled]="itemLabel.length < 27"
matTooltipPosition="after"
[attr.aria-label]="item.label"
[matTooltipClass]="'API-tooltip'"
@@ -112,7 +106,7 @@
item.children && item.level === expandableLevel() && !item.isExpanded
"
[matTooltip]="item.label"
[matTooltipDisabled]="isMobile || itemLabel.length < labelTruncationThreshold"
[matTooltipDisabled]="itemLabel.length < 27"
matTooltipPosition="after"
[attr.aria-label]="item.label"
[matTooltipClass]="'API-tooltip'"
@@ -162,12 +162,11 @@ a,
}
}
.docs-external-link.docs-faceted-list-item-text {
.docs-external-link {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
max-width: 100%;
gap: 0.5rem;
&::after {
content: 'open_in_new';
@@ -175,11 +174,7 @@ a,
font-size: 1.1rem;
color: var(--quinary-contrast);
transition: color 0.3s ease;
margin-inline-end: 0.2rem;
}
a:hover &::after {
color: var(--primary-contrast);
margin-inline-end: 0.4rem;
}
}
@@ -193,12 +188,8 @@ a,
}
.docs-navigation-group {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 0.5rem 0.5rem 1rem;
background: var(--septenary-contrast);
&:not(:first-child) {
margin-top: 2rem;
}
@@ -31,32 +31,6 @@ const navigationItems: NavigationItem[] = [
},
];
const navigationItemsWithCategoryStatus: NavigationItem[] = [
{
label: 'Forms',
level: 1,
categoriesStatus: [{'Signal Forms': 'new'}],
children: [],
},
];
navigationItemsWithCategoryStatus[0].children = [
{
label: 'Overview',
path: 'guide/forms',
level: 2,
category: 'Signal Forms',
parent: navigationItemsWithCategoryStatus[0],
},
{
label: 'Signals',
path: 'guide/forms/signals',
level: 2,
category: 'Signal Forms',
parent: navigationItemsWithCategoryStatus[0],
},
];
describe('NavigationList', () => {
let component: NavigationList;
let fixture: ComponentFixture<NavigationList>;
@@ -84,20 +58,6 @@ describe('NavigationList', () => {
expect(nonClickableItem.length).toBe(1);
});
it('should show category status on grouped navigation headers', async () => {
fixture.componentRef.setInput('navigationItems', [...navigationItemsWithCategoryStatus]);
await fixture.whenStable();
const categoryGroup = fixture.debugElement.query(By.css('.docs-navigation-group'));
const categoryStatus = fixture.debugElement.query(
By.css('.docs-navigation-group .docs-new-item'),
);
expect(categoryGroup.nativeElement.innerText).toContain('Signal Forms');
expect(categoryStatus).toBeTruthy();
expect(categoryStatus.nativeElement.innerText).toBe('New');
});
it('should append `docs-navigation-list-dropdown` when isDropdownView is true', async () => {
fixture.componentRef.setInput('isDropdownView', true);
await fixture.whenStable();
@@ -7,13 +7,12 @@
*/
import {NgTemplateOutlet} from '@angular/common';
import {Component, inject, input, output} from '@angular/core';
import {ChangeDetectionStrategy, Component, inject, input, output} from '@angular/core';
import {MatTooltip} from '@angular/material/tooltip';
import {RouterLink, RouterLinkActive} from '@angular/router';
import {NavigationItem} from '../../interfaces/index';
import {IsActiveNavigationItem} from '../../pipes';
import {NavigationState} from '../../services/index';
import {isMobile} from '../../utils';
import {IconComponent} from '../icon/icon.component';
@Component({
@@ -28,6 +27,7 @@ import {IconComponent} from '../icon/icon.component';
],
templateUrl: './navigation-list.component.html',
styleUrls: ['./navigation-list.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class NavigationList {
readonly navigationItems = input.required<NavigationItem[]>();
@@ -39,15 +39,11 @@ export class NavigationList {
readonly linkClicked = output<void>();
protected readonly labelTruncationThreshold = 27;
private readonly navigationState = inject(NavigationState);
private readonly crossCategoryOrigin = this.navigationState.crossCategoryOrigin;
readonly activeItem = this.navigationState.activeNavigationItem;
protected readonly isMobile = isMobile;
toggle(item: NavigationItem): void {
if (
item.level === 1 &&
@@ -76,27 +72,14 @@ export class NavigationList {
return items.some((item) => !!item.category);
}
private getCategoryStatus(item: NavigationItem, category: string): 'new' | 'updated' | undefined {
const categoriesStatus = item.parent?.categoriesStatus;
if (!categoriesStatus) {
return undefined;
}
return categoriesStatus.find((status) => status[category])?.[category];
}
protected groupItems(
items: NavigationItem[],
preserveOtherCategoryOrder: boolean,
): Map<string, {items: NavigationItem[]; status: 'new' | 'updated' | undefined}> {
): Map<string, NavigationItem[]> {
const hasCategories = this.hasCategories(items);
if (hasCategories) {
const others: NavigationItem[] = [];
const categorizedItems = new Map<
string,
{items: NavigationItem[]; status: 'new' | 'updated' | undefined}
>();
const categorizedItems = new Map<string, NavigationItem[]>();
for (const item of items) {
const category = item.category || 'Other';
if (!preserveOtherCategoryOrder && category === 'Other') {
@@ -104,20 +87,16 @@ export class NavigationList {
continue;
}
if (!categorizedItems.has(category)) {
categorizedItems.set(category, {
items: [],
status: this.getCategoryStatus(item, category),
});
categorizedItems.set(category, []);
}
categorizedItems.get(category)!.items.push(item);
categorizedItems.get(category)!.push(item);
}
if (others.length) {
categorizedItems.set('Other', {items: others, status: undefined});
categorizedItems.set('Other', others);
}
return categorizedItems;
} else {
return new Map([['', {items, status: undefined}]]);
return new Map([['', items]]);
}
}
}
@@ -29,7 +29,6 @@ ng_project(
"//adev/shared-docs/pipes",
"//adev/shared-docs/providers",
"//adev/shared-docs/services",
"//adev/shared-docs/utils",
],
)
@@ -1,4 +1,4 @@
<dialog #searchDialog id="docsSearchDialog">
<dialog #searchDialog>
<div class="docs-search-container" (docsClickOutside)="closeSearchDialog()">
<docs-text-field
[autofocus]="true"
@@ -65,22 +65,12 @@
<docs-search-history />
} @else {
<div class="docs-search-results docs-mini-scroll-track">
@if (emptyState() === 'start') {
@if (!resultsResource.hasValue()) {
<div class="docs-search-results__start-typing">
<img
src="assets/images/angie/magnifying-glass.svg"
class="docs-search-angie"
aria-hidden="true"
/>
<span>Start typing to see results</span>
</div>
} @else {
} @else if (searchResults().length === 0) {
<div class="docs-search-results__no-results">
<img
src="assets/images/angie/question.svg"
class="docs-search-angie"
aria-hidden="true"
/>
<span>No results found</span>
</div>
}
@@ -150,20 +150,10 @@ dialog {
.docs-search-results__start-typing,
.docs-search-results__no-results {
display: flex;
flex-direction: column;
align-items: center;
padding: 0.75rem;
color: var(--quaternary-contrast);
}
.docs-search-angie {
width: 210px;
height: auto;
aspect-ratio: 1;
margin-block: -1rem -0.5rem;
}
.docs-search-footer {
display: flex;
align-items: center;
@@ -14,7 +14,7 @@ import {Router, provideRouter} from '@angular/router';
import {SearchDialog} from './search-dialog.component';
import {ENVIRONMENT, WINDOW} from '../../providers';
import {ALGOLIA_CLIENT, Search} from '../../services';
import {FakeEventTarget, timeout, useAutoTick} from '../../testing/index';
import {FakeEventTarget} from '../../testing/index';
import {AlgoliaIcon} from '../algolia-icon/algolia-icon.component';
import {SearchResult} from '../../interfaces';
@@ -27,10 +27,8 @@ describe('SearchDialog', () => {
let search: Search;
useAutoTick();
beforeEach(async () => {
searchResults.and.returnValue(Promise.resolve({results: [{hits: []}]}));
searchResults.and.returnValue([]);
TestBed.configureTestingModule({
imports: [SearchDialog],
@@ -57,9 +55,6 @@ describe('SearchDialog', () => {
// Fire the request
TestBed.inject(ApplicationRef).tick();
// The delay from debounced (200ms)
await timeout(300);
// Wait for the resource to resolve
await TestBed.inject(ApplicationRef).whenStable();
@@ -90,9 +85,6 @@ describe('SearchDialog', () => {
// Fire the request
TestBed.inject(ApplicationRef).tick();
// The delay from debounced (200ms)
await timeout(300);
// Wait for the resource to resolve
await TestBed.inject(ApplicationRef).whenStable();
@@ -104,7 +96,7 @@ describe('SearchDialog', () => {
});
it('should display `Start typing to see results` message when there are no provided query', () => {
searchResults.and.returnValue(Promise.resolve(undefined));
searchResults.and.returnValue(undefined);
const startTypingContainer = fixture.debugElement.query(
By.css('.docs-search-results__start-typing'),
@@ -113,35 +105,6 @@ describe('SearchDialog', () => {
expect(startTypingContainer).toBeTruthy();
});
it('should keep the `No results found` message while re-querying instead of flickering back to `Start typing`', async () => {
const appRef = TestBed.inject(ApplicationRef);
// Settle on an empty result set so `No results found` is shown.
search.searchQuery.set('foobarbaz');
searchResults.and.returnValue(Promise.resolve({results: [{hits: []}]}));
appRef.tick();
await timeout(300);
await appRef.whenStable();
expect(fixture.debugElement.query(By.css('.docs-search-results__no-results'))).toBeTruthy();
// Edit the query so a new search goes in flight and stays pending.
let resolveReload!: (value: unknown) => void;
searchResults.and.returnValue(new Promise((resolve) => (resolveReload = resolve)));
search.searchQuery.set('foobarba');
appRef.tick();
// The delay from debounced (200ms), after which the pending request is loading.
await timeout(300);
// While the re-query is loading it must not revert to the `Start typing` state.
expect(fixture.debugElement.query(By.css('.docs-search-results__start-typing'))).toBeNull();
expect(fixture.debugElement.query(By.css('.docs-search-results__no-results'))).toBeTruthy();
resolveReload({results: [{hits: []}]});
await appRef.whenStable();
});
it('should display list of the search results when results exist', async () => {
search.searchQuery.set('fakeQuery');
searchResults.and.returnValue(Promise.resolve({results: [{hits: fakeSearchResults}]}));
@@ -149,9 +112,6 @@ describe('SearchDialog', () => {
// Fire the request
TestBed.inject(ApplicationRef).tick();
// The delay from debounced (200ms)
await timeout(300);
// Wait for the resource to resolve
await TestBed.inject(ApplicationRef).whenStable();
@@ -8,6 +8,7 @@
import {
afterNextRender,
ChangeDetectionStrategy,
Component,
DestroyRef,
effect,
@@ -32,10 +33,10 @@ import {RelativeLink} from '../../pipes';
import {AlgoliaIcon} from '../algolia-icon/algolia-icon.component';
import {SearchHistoryComponent} from '../search-history/search-history.component';
import {TextField} from '../text-field/text-field.component';
import {getRelativeUrl} from '../../utils';
@Component({
selector: 'docs-search-dialog',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
ClickOutside,
TextField,
@@ -56,7 +57,7 @@ export class SearchDialog {
readonly history = inject(SearchHistory);
private readonly search = inject(Search);
private readonly relativeLink = new RelativeLink();
private readonly router = inject(Router);
private readonly window = inject(WINDOW);
private readonly injector = inject(Injector);
@@ -67,7 +68,6 @@ export class SearchDialog {
readonly resultsResource = this.search.resultsResource;
readonly searchResults = this.search.searchResults;
readonly emptyState = this.search.emptyState;
searchForm = form(this.search.searchQuery);
@@ -122,7 +122,7 @@ export class SearchDialog {
return;
}
this.router.navigateByUrl(getRelativeUrl(activeItemLink));
this.router.navigateByUrl(this.relativeLink.transform(activeItemLink));
this.onClose.emit();
}
}
@@ -28,7 +28,6 @@ ng_project(
"//adev/shared-docs/directives",
"//adev/shared-docs/pipes",
"//adev/shared-docs/services",
"//adev/shared-docs/utils",
],
)
@@ -6,10 +6,9 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {ActiveDescendantKeyManager} from '@angular/cdk/a11y';
import {NgTemplateOutlet} from '@angular/common';
import {
afterNextRender,
ChangeDetectionStrategy,
Component,
DestroyRef,
effect,
@@ -17,13 +16,14 @@ import {
Injector,
viewChildren,
} from '@angular/core';
import {toSignal} from '@angular/core/rxjs-interop';
import {Router, RouterLink} from '@angular/router';
import {toSignal} from '@angular/core/rxjs-interop';
import {ActiveDescendantKeyManager} from '@angular/cdk/a11y';
import {NgTemplateOutlet} from '@angular/common';
import {SearchItem} from '../../directives';
import {RelativeLink} from '../../pipes';
import {SearchHistory} from '../../services';
import {getRelativeUrl} from '../../utils';
import {RelativeLink} from '../../pipes';
import {SearchItem} from '../../directives';
@Component({
selector: 'docs-search-history',
@@ -34,6 +34,7 @@ import {getRelativeUrl} from '../../utils';
'(document:keydown)': 'onKeydown($event)',
'(document:mousemove)': 'onMouseMove($event)',
},
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SearchHistoryComponent {
protected readonly items = viewChildren(SearchItem);
@@ -42,6 +43,7 @@ export class SearchHistoryComponent {
private readonly injector = inject(Injector);
private readonly router = inject(Router);
private readonly relativeLink = new RelativeLink();
private readonly keyManager = new ActiveDescendantKeyManager(
this.items,
this.injector,
@@ -99,7 +101,7 @@ export class SearchHistoryComponent {
const activeItemLink = this.keyManager.activeItem?.item()?.url;
if (activeItemLink) {
const url = getRelativeUrl(activeItemLink);
const url = this.relativeLink.transform(activeItemLink);
this.router.navigateByUrl(url);
}
}
@@ -16,7 +16,6 @@ ng_project(
],
deps = [
"//adev:node_modules/@angular/aria",
"//adev:node_modules/@angular/cdk",
"//adev:node_modules/@angular/common",
"//adev:node_modules/@angular/core",
"//adev:node_modules/@angular/forms",
@@ -1,94 +1,61 @@
<div ngCombobox #combobox="ngCombobox" class="docs-combobox-container" [(expanded)]="popupExpanded">
<div #origin class="docs-select-input-container">
<div ngCombobox #outerCombobox="ngCombobox" [readonly]="true" [disabled]="disabled()">
<div class="docs-select-input-container">
<input
ngComboboxInput
[attr.id]="id()"
[attr.name]="name()"
[value]="displayValue()"
placeholder="Select an option"
placeholder="Select an option..."
readonly
[tabindex]="-1"
/>
<span class="material-symbols-outlined docs-select-arrow" translate="no" aria-hidden="true"
>arrow_drop_down</span
>
</div>
<ng-template
[cdkConnectedOverlay]="{origin: combobox.element, usePopover: 'inline', matchWidth: true}"
[cdkConnectedOverlayOpen]="popupExpanded()"
[cdkConnectedOverlayDisableClose]="false"
(overlayOutsideClick)="popupExpanded.set(false)"
>
<ng-template ngComboboxPopup [combobox]="combobox" popupType="dialog">
<div class="docs-select-popover">
<div class="docs-select-dialog" ngComboboxWidget>
<div class="example-combobox-container">
<div class="docs-select-search-container">
<span
class="material-symbols-outlined docs-select-search-icon"
translate="no"
aria-hidden="true"
>search</span
>
<input
ngCombobox
#innerCombobox="ngCombobox"
#searchInput
class="docs-select-search-input"
placeholder="Search..."
[(value)]="searchString"
[alwaysExpanded]="true"
(keydown.escape)="onSearchEscape($event)"
/>
</div>
<div aria-live="polite" class="cdk-visually-hidden">
{{ filteredOptions().length === 0 ? 'No results found for ' + searchString() : '' }}
</div>
<ng-template ngComboboxPopup [combobox]="innerCombobox">
<div class="example-popup example-popup-no-margin">
@if (filteredOptions().length === 0) {
<div class="docs-select-no-results">
<img
src="assets/images/angie/question.svg"
class="docs-select-no-results-angie"
aria-hidden="true"
/>
<span>No results found</span>
</div>
}
<div
#listbox="ngListbox"
ngListbox
[(value)]="selectedValues"
[multi]="false"
ngComboboxWidget
class="docs-select-listbox"
focusMode="activedescendant"
tabindex="-1"
selectionMode="explicit"
(click)="onCommit()"
(keydown.enter)="onCommit()"
[activeDescendant]="listbox.activeDescendant()"
[class.example-empty]="filteredOptions().length === 0"
>
@for (option of filteredOptions(); track option.value) {
<div
ngOption
[value]="option.value"
[label]="option.label"
class="docs-select-option"
>
<span class="docs-select-option-label">{{ option.label }}</span>
</div>
}
</div>
</div>
</ng-template>
</div>
<ng-template ngComboboxPopupContainer>
<dialog ngComboboxDialog class="docs-select-dialog">
<div ngCombobox #innerCombobox="ngCombobox" filterMode="manual" [alwaysExpanded]="true">
<div class="docs-select-search-container">
<span
class="material-symbols-outlined docs-select-search-icon"
translate="no"
aria-hidden="true"
>search</span
>
<input
ngComboboxInput
class="docs-select-search-input"
placeholder="Search..."
[(value)]="searchString"
/>
</div>
<ng-template ngComboboxPopupContainer>
@if (filteredOptions().length === 0) {
<div class="docs-select-no-results">No results found</div>
}
<div ngListbox [(values)]="selectedValues" class="docs-select-listbox">
@for (option of filteredOptions(); track option.value) {
<div
ngOption
[value]="option.value"
[label]="option.label"
class="docs-select-option"
>
<span class="docs-select-option-label">{{ option.label }}</span>
<span
class="material-symbols-outlined docs-select-check-icon"
translate="no"
aria-hidden="true"
>check</span
>
</div>
}
</div>
</ng-template>
</div>
</ng-template>
</dialog>
</ng-template>
</div>
@@ -1,16 +1,14 @@
:host {
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
}
[ngCombobox] {
position: relative;
width: 100%;
display: flex;
flex-direction: column;
}
.docs-combobox-container {
border: 1px solid var(--border-color);
border-radius: 0.25rem;
overflow: hidden;
}
.docs-select-input-container {
@@ -18,25 +16,15 @@
position: relative;
align-items: center;
border-radius: 0.25rem;
}
input {
width: 100%;
border: none;
outline: none;
font-size: 1rem;
padding: 0.7rem 1rem 0.7rem;
background-color: var(--septenary-contrast);
color: var(--primary-contrast);
[ngComboboxInput] {
border-radius: 0.25rem;
}
&[readonly] {
cursor: pointer;
padding: 0.7rem 1rem;
}
&[aria-expanded='true'] + .docs-select-arrow {
transform: rotate(180deg);
}
}
[ngComboboxInput][readonly='true'] {
cursor: pointer;
padding: 0.7rem 1rem;
}
[ngCombobox]:focus-within [ngComboboxInput]:not(.docs-select-search-input) {
@@ -44,10 +32,6 @@
box-shadow: 0 0 0 4px color-mix(in srgb, var(--vivid-pink) 25%, transparent);
}
.docs-select-popover {
width: 100%;
}
.docs-select-arrow {
width: 24px;
height: 24px;
@@ -62,19 +46,22 @@
transition: transform 0.2s ease;
}
[ngComboboxInput] [ngCombobox] {
[ngComboboxInput][aria-expanded='true'] + .docs-select-arrow {
transform: rotate(180deg);
}
[ngComboboxInput] {
width: 100%;
border: none;
outline: none;
font-size: 1rem;
padding: 0.7rem 1rem 0.7rem;
padding: 0.7rem 1rem 0.7rem 2.5rem;
background-color: var(--septenary-contrast);
color: var(--primary-contrast);
}
.docs-select-dialog {
position: absolute;
box-sizing: border-box;
width: 100%;
left: auto;
right: auto;
top: auto;
@@ -82,7 +69,6 @@
padding: 0;
border: 1px solid var(--border-color);
border-radius: 0.25rem;
overflow: hidden;
background-color: var(--septenary-contrast);
color: inherit;
@@ -149,10 +135,6 @@
}
.docs-select-no-results {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.25rem;
padding: 0.75rem;
text-align: center;
font-size: 0.875rem;
@@ -160,18 +142,11 @@
opacity: 0.7;
}
.docs-select-no-results-angie {
width: 88px;
height: auto;
aspect-ratio: 1;
}
.docs-select-listbox {
display: flex;
flex-direction: column;
max-height: 12rem;
overflow: auto;
overscroll-behavior: contain;
padding: 0.25rem;
}
@@ -196,8 +171,16 @@
color: var(--vivid-pink);
background-color: color-mix(in srgb, var(--vivid-pink) 5%, transparent);
}
&:not([aria-selected='true']) .docs-select-check-icon {
display: none;
}
}
.docs-select-option-label {
flex: 1;
}
.docs-select-check-icon {
font-size: 0.9rem;
}
@@ -1,4 +1,4 @@
/**
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
@@ -6,21 +6,28 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Combobox, ComboboxPopup, ComboboxWidget} from '@angular/aria/combobox';
import {
Combobox,
ComboboxDialog,
ComboboxInput,
ComboboxPopupContainer,
} from '@angular/aria/combobox';
import {Listbox, Option} from '@angular/aria/listbox';
import {OverlayModule} from '@angular/cdk/overlay';
import {
afterRenderEffect,
ChangeDetectionStrategy,
Component,
computed,
input,
model,
signal,
untracked,
viewChild,
} from '@angular/core';
import {FormValueControl} from '@angular/forms/signals';
import {FormsModule} from '@angular/forms';
type SelectOptionValue = string;
type SelectOptionValue = string | number | boolean;
export interface SelectOption {
label: string;
@@ -29,9 +36,18 @@ export interface SelectOption {
@Component({
selector: 'docs-select',
templateUrl: 'select.component.html',
styleUrl: 'select.component.css',
imports: [Combobox, ComboboxPopup, ComboboxWidget, Listbox, Option, OverlayModule],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
Combobox,
ComboboxDialog,
ComboboxInput,
ComboboxPopupContainer,
FormsModule,
Listbox,
Option,
],
templateUrl: './select.component.html',
styleUrls: ['./select.component.scss'],
})
export class Select implements FormValueControl<string | null> {
readonly value = model<string | null>(null);
@@ -39,13 +55,14 @@ export class Select implements FormValueControl<string | null> {
readonly id = input.required<string>({alias: 'selectId'});
readonly name = input.required<string>();
readonly options = input.required<SelectOption[]>();
readonly disabled = input(false);
readonly listbox = viewChild(Listbox);
readonly combobox = viewChild(Combobox);
readonly dialog = viewChild(ComboboxDialog);
readonly listbox = viewChild<Listbox<SelectOptionValue>>(Listbox);
readonly combobox = viewChild<Combobox<SelectOptionValue>>(Combobox);
readonly searchString = signal('');
readonly popupExpanded = signal(false);
readonly filteredOptions = computed(() => {
const search = this.searchString().toLowerCase();
if (!search) {
@@ -67,36 +84,39 @@ export class Select implements FormValueControl<string | null> {
constructor() {
afterRenderEffect(() => {
this.listbox()?.scrollActiveItemIntoView();
if (this.dialog() && this.combobox()?.expanded()) {
untracked(() => this.listbox()?.gotoFirst());
this.positionDialog();
}
});
}
onCommit() {
const values = this.selectedValues();
if (values.length) {
this.value.set(values[0]);
this.popupExpanded.set(false);
afterRenderEffect(() => {
const selected = this.selectedValues();
if (selected.length > 0) {
untracked(() => this.dialog()?.close());
this.value.set(selected[0] as string);
this.searchString.set('');
}
});
afterRenderEffect(() => this.listbox()?.scrollActiveItemIntoView());
}
// TODO: Improve once CDK overlay is fixed https://github.com/angular/components/issues/32504
private positionDialog(): void {
const dialog = this.dialog();
const combobox = this.combobox();
if (!dialog || !combobox) {
return;
}
}
/** Dismisses the dialog overlay on Escape key. */
onSearchEscape(event: Event) {
this.popupExpanded.set(false);
this.combobox()?.element.focus();
}
const comboboxRect = combobox.inputElement()?.getBoundingClientRect();
const scrollY = window.scrollY;
/** Handles keydown events on the clear button. */
onKeydown(event: KeyboardEvent): void {
if (event.key === 'Enter') {
this.clear();
this.popupExpanded.set(false);
event.stopPropagation();
if (comboboxRect) {
dialog.element.style.width = `${comboboxRect.width}px`;
dialog.element.style.top = `${comboboxRect.bottom + scrollY + 4}px`;
dialog.element.style.left = `${comboboxRect.left}px`;
}
}
/** Clears the search query and all selected options. */
clear(): void {
this.searchString.set('');
this.value.set(null);
}
}
@@ -6,11 +6,12 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, input, model} from '@angular/core';
import {ChangeDetectionStrategy, Component, input, model} from '@angular/core';
import {FormCheckboxControl} from '@angular/forms/signals';
@Component({
selector: 'docs-slide-toggle',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './slide-toggle.component.html',
styleUrls: ['./slide-toggle.component.scss'],
})
@@ -8,6 +8,7 @@
import {
afterRenderEffect,
ChangeDetectionStrategy,
Component,
computed,
ElementRef,
@@ -32,6 +33,7 @@ let idCounter = 0;
host: {
class: 'docs-tab-group',
},
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TabGroup {
private readonly _renderer = inject(Renderer2);
@@ -6,14 +6,23 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {
ChangeDetectionStrategy,
Component,
DestroyRef,
input,
inject,
afterNextRender,
signal,
} from '@angular/core';
import {Location, ViewportScroller} from '@angular/common';
import {afterNextRender, Component, DestroyRef, inject, input, signal} from '@angular/core';
import {TableOfContentsLevel} from '../../interfaces/index';
import {TableOfContentsLoader} from '../../services';
import {IconComponent} from '../icon/icon.component';
@Component({
selector: 'docs-table-of-contents',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: './table-of-contents.component.html',
styleUrls: ['./table-of-contents.component.scss'],
imports: [IconComponent],
@@ -6,12 +6,21 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {afterNextRender, Component, ElementRef, input, model, viewChild} from '@angular/core';
import {
afterNextRender,
ChangeDetectionStrategy,
Component,
ElementRef,
input,
model,
viewChild,
} from '@angular/core';
import {FormValueControl} from '@angular/forms/signals';
import {IconComponent} from '../icon/icon.component';
@Component({
selector: 'docs-text-field',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [IconComponent],
templateUrl: './text-field.component.html',
styleUrls: ['./text-field.component.scss'],
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Component, inject, input, linkedSignal} from '@angular/core';
import {ChangeDetectionStrategy, Component, inject, input, linkedSignal} from '@angular/core';
import {ExternalLink} from '../../directives';
import {LOCAL_STORAGE} from '../../providers';
import {IconComponent} from '../icon/icon.component';
@@ -18,6 +18,7 @@ export const STORAGE_KEY_PREFIX = 'docs-was-closed-top-banner-';
imports: [ExternalLink, IconComponent],
templateUrl: './top-level-banner.component.html',
styleUrl: './top-level-banner.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class TopLevelBannerComponent {
private readonly localStorage = inject(LOCAL_STORAGE);
@@ -71,7 +71,6 @@ ts_project(
"//adev/shared-docs/interfaces",
"//adev/shared-docs/providers",
"//adev/shared-docs/services",
"//adev/shared-docs/utils",
],
)
@@ -132,7 +132,6 @@
.docs-page-title {
display: flex;
justify-content: space-between;
align-items: baseline;
h1 {
margin-block: 0;
@@ -20,7 +20,6 @@ import {CopySourceCodeButton} from '../../copy-source-code-button/copy-source-co
import {CopyLinkButton} from '../../copy-link-anchor/copy-link-anchor.component';
import {TableOfContents} from '../../table-of-contents/table-of-contents.component';
import {Clipboard} from '@angular/cdk/clipboard';
import {isFirefox} from '../../../utils';
describe('DocViewer', () => {
let exampleContentSpy: jasmine.SpyObj<ExampleViewerContentLoader>;
@@ -66,23 +65,6 @@ describe('DocViewer', () => {
</div>
`;
const exampleContentWithVideoFacade = `
<div class="docs-video-container">
<a
class="docs-video-facade"
href="https://www.youtube.com/watch?v=abc123&autoplay=1"
target="_blank"
rel="noopener"
aria-label="Play video: Test video"
data-video-src="https://www.youtube.com/embed/abc123"
data-video-title="Test video"
>
<img class="docs-video-thumbnail" src="https://i.ytimg.com/vi/abc123/maxresdefault.jpg" alt="" loading="lazy" />
<span class="docs-video-play-button" aria-hidden="true"></span>
</a>
</div>
`;
const exampleContentWithHeadings = `
<h2>Heading h2</h2>
<h3>Heading h3</h3>
@@ -238,16 +220,6 @@ describe('DocViewer', () => {
expect(copyButton).toBeTruthy();
});
it('should not leak any icon text content (so it stays out of the search index)', () => {
const fixture = TestBed.createComponent(CopyLinkButton);
fixture.componentRef.setInput('href', '#test-section');
fixture.componentRef.setInput('label', 'Test Section');
fixture.componentRef.setInput('matTooltip', 'Copy link to Test Section');
fixture.detectChanges();
expect(fixture.nativeElement.textContent.trim()).toBe('');
});
it('should copy link to clipboard when copy button is clicked', async () => {
const clipboard = TestBed.inject(Clipboard);
const clipboardSpy = spyOn(clipboard, 'copy').and.returnValue(true);
@@ -265,25 +237,4 @@ describe('DocViewer', () => {
// necessary to undo this location change by going back in the history.
window.history.back();
});
it('should upgrade a video facade to an iframe in browsers that can embed it', async () => {
const fixture = TestBed.createComponent(DocViewer);
fixture.componentRef.setInput('docContent', exampleContentWithVideoFacade);
await fixture.whenStable();
const iframe = fixture.nativeElement.querySelector('iframe.docs-video');
const facade = fixture.nativeElement.querySelector('a.docs-video-facade');
if (isFirefox) {
// Firefox can't load the cross-origin embed under COEP, so the facade stays a link.
expect(facade).toBeTruthy();
expect(iframe).toBeNull();
} else {
expect(iframe).toBeTruthy();
expect(iframe.getAttribute('src')).toContain('youtube.com/embed/abc123');
expect(iframe.hasAttribute('credentialless')).toBeTrue();
expect(facade).toBeNull();
}
});
});
@@ -9,6 +9,7 @@
import {DOCUMENT, isPlatformBrowser, Location} from '@angular/common';
import {
ApplicationRef,
ChangeDetectionStrategy,
Component,
ComponentRef,
createComponent,
@@ -31,16 +32,16 @@ import {Router} from '@angular/router';
import {fromEvent} from 'rxjs';
import {Snippet} from '../../../interfaces';
import {NavigationState, TOC_SKIP_CONTENT_MARKER} from '../../../services';
import {handleHrefClickEventWithRouter, isFirefox} from '../../../utils';
import {handleHrefClickEventWithRouter} from '../../../utils';
import {IconComponent} from '../../icon/icon.component';
import {TableOfContents} from '../../table-of-contents/table-of-contents.component';
import {DomSanitizer} from '@angular/platform-browser';
import {Breadcrumb} from '../../breadcrumb/breadcrumb.component';
import {CopyLinkButton} from '../../copy-link-anchor/copy-link-anchor.component';
import {CopySourceCodeButton} from '../../copy-source-code-button/copy-source-code-button.component';
import {TabGroup} from '../../tab-group/tab-group.component';
import {CopyLinkButton} from '../../copy-link-anchor/copy-link-anchor.component';
import {ExampleViewer} from '../example-viewer/example-viewer.component';
import {TabGroup} from '../../tab-group/tab-group.component';
const TOC_HOST_ELEMENT_NAME = 'docs-table-of-contents';
export const ASSETS_EXAMPLES_PATH = 'assets/content/examples';
@@ -55,6 +56,7 @@ const GITHUB_CONTENT_URL = 'https://github.com/angular/angular/blob/{{BUILD_SCM_
selector: DOCS_VIEWER_SELECTOR,
template: '',
styleUrls: ['docs-viewer.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
'[class.docs-animate-content]': 'animateContent',
@@ -124,7 +126,6 @@ export class DocViewer {
// In case when content contains tabs, create tabs component and move
// content in a tab into tab panel.
this.constructTabs(contentContainer);
this.setupVideoFacades(contentContainer);
}
// Display Breadcrumb component if the `<docs-breadcrumb>` element exists
@@ -413,36 +414,4 @@ export class DocViewer {
tabGroup.parentElement!.replaceChild(tabGroupRef.location.nativeElement, tabGroup);
}
}
private setupVideoFacades(element: HTMLElement): void {
const facades = element.querySelectorAll<HTMLAnchorElement>('a.docs-video-facade');
for (const facade of Array.from(facades)) {
const src = facade.getAttribute('data-video-src');
if (!src) {
continue;
}
if (isFirefox) {
const thumbnail = facade.querySelector<HTMLImageElement>('img.docs-video-thumbnail');
thumbnail?.addEventListener(
'error',
() => {
thumbnail.src = thumbnail.src.replace('maxresdefault', 'hqdefault');
},
{once: true},
);
continue;
}
const iframe = this.document.createElement('iframe');
iframe.className = 'docs-video';
iframe.src = src;
iframe.title = facade.getAttribute('data-video-title') ?? 'Video player';
iframe.setAttribute('allow', 'accelerometer; encrypted-media; gyroscope; picture-in-picture');
iframe.setAttribute('allowfullscreen', '');
iframe.setAttribute('credentialless', '');
facade.replaceWith(iframe);
}
}
}
@@ -5,7 +5,6 @@
#codeTabs
animationDuration="0ms"
mat-stretch-tabs="false"
[selectedIndex]="selectedTabIndex()"
(selectedTabChange)="onTabIndexChange($event.index)"
>
@for (tab of tabs(); track tab) {
@@ -22,7 +21,7 @@
[class.docs-example-code-toggle__show]="!showCode()"
[attr.aria-checked]="showCode()"
[attr.aria-label]="showCode() ? 'Hide code' : 'Show code'"
(click)="toggleCodeVisibility()"
(click)="showCode.set(!showCode())"
[matTooltip]="showCode() ? 'Hide code' : 'Show code'"
matTooltipPosition="above"
>
@@ -166,147 +166,6 @@ describe('ExampleViewer', () => {
expect(hiddenLine).toBeNull();
});
it('keeps the expand control after switching tabs while expanded', async () => {
const collapsible = {
name: 'example.ts',
sanitizedContent:
'<pre><code><div class="line">one</div><div class="line">two</div></code></pre>',
visibleLinesRange: '1',
};
componentRef.setInput(
'metadata',
getMetadata({files: [collapsible, {name: 'example.css', sanitizedContent: ''}]}),
);
await component.renderExample();
await fixture.whenStable();
expect(component.expandable()).toBeTrue();
component.toggleExampleVisibility();
await fixture.whenStable();
const tabGroup = await loader.getHarness(MatTabGroupHarness);
await (await tabGroup.getTabs())[1].select();
await fixture.whenStable();
await (await tabGroup.getTabs())[0].select();
await fixture.whenStable();
expect(component.expandable()).toBeTrue();
});
it('hides only the lines outside the visible range, and adds no gap within one range', async () => {
componentRef.setInput(
'metadata',
getMetadata({
files: [
{
name: 'example.ts',
sanitizedContent:
'<pre><code><div class="line">one</div><div class="line">two</div><div class="line">three</div></code></pre>',
visibleLinesRange: '2,3',
},
],
}),
);
await component.renderExample();
await fixture.whenStable();
const lines = fixture.debugElement
.queryAll(By.css('.line'))
.map((line) => (line.nativeElement as HTMLElement).className);
expect(lines).toEqual(['line hidden', 'line', 'line']);
});
it('hides the line numbers that pair with the hidden lines', async () => {
const numbered = (n: number, text: string) =>
`<span class="shiki-ln-number">${n}</span><div class="line">${text}</div>`;
componentRef.setInput(
'metadata',
getMetadata({
files: [
{
name: 'example.ts',
sanitizedContent: `<pre><code>${numbered(1, 'one')}${numbered(2, 'two')}${numbered(3, 'three')}</code></pre>`,
visibleLinesRange: '2,3',
},
],
}),
);
await component.renderExample();
await fixture.whenStable();
const visibleNumbers = fixture.debugElement
.queryAll(By.css('.shiki-ln-number'))
.filter((el) => !(el.nativeElement as HTMLElement).classList.contains('hidden'))
.map((el) => (el.nativeElement as HTMLElement).textContent);
expect(visibleNumbers).toEqual(['2', '3']);
});
it('restores the selected tab and its collapsed lines after hiding and reshowing the code', async () => {
componentRef.setInput(
'metadata',
getMetadata({
files: [
{name: 'example.ts', sanitizedContent: ''},
{
name: 'example.css',
sanitizedContent:
'<pre><code><div class="line">one</div><div class="line">two</div></code></pre>',
visibleLinesRange: '2',
},
],
}),
);
await component.renderExample();
await fixture.whenStable();
const tabGroup = await loader.getHarness(MatTabGroupHarness);
await (await tabGroup.getTabs())[1].select();
await fixture.whenStable();
component.toggleCodeVisibility();
await fixture.whenStable();
component.toggleCodeVisibility();
await fixture.whenStable();
expect(
await (await (await loader.getHarness(MatTabGroupHarness)).getSelectedTab()).getLabel(),
).toBe('CSS');
const lines = fixture.debugElement
.queryAll(By.css('.line'))
.map((line) => (line.nativeElement as HTMLElement).className);
expect(lines).toEqual(['line hidden', 'line']);
});
it('offers no expand control when the range covers the whole file', async () => {
componentRef.setInput(
'metadata',
getMetadata({
files: [
{
name: 'example.ts',
sanitizedContent:
'<pre><code><div class="line">one</div><div class="line">two</div></code></pre>',
visibleLinesRange: '1,2',
},
],
}),
);
await component.renderExample();
await fixture.whenStable();
expect(component.expandable()).toBeFalse();
});
it('should set exampleComponent when metadata contains path and preview is true', async () => {
exampleContentSpy.loadPreview.and.resolveTo(ExampleComponent);
componentRef.setInput(
@@ -6,10 +6,9 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Clipboard} from '@angular/cdk/clipboard';
import {DOCUMENT, NgComponentOutlet, NgTemplateOutlet} from '@angular/common';
import {
afterNextRender,
ChangeDetectionStrategy,
Component,
computed,
ElementRef,
@@ -19,12 +18,14 @@ import {
signal,
Type,
} from '@angular/core';
import {DOCUMENT, NgComponentOutlet, NgTemplateOutlet} from '@angular/common';
import {MatTab, MatTabGroup} from '@angular/material/tabs';
import {MatTooltip} from '@angular/material/tooltip';
import {ExampleMetadata, Snippet} from '../../../interfaces/index';
import {EXAMPLE_VIEWER_CONTENT_LOADER} from '../../../providers/index';
import {Clipboard} from '@angular/cdk/clipboard';
import {CopySourceCodeButton} from '../../copy-source-code-button/copy-source-code-button.component';
import {IconComponent} from '../../icon/icon.component';
import {ExampleMetadata, Snippet} from '../../../interfaces/index';
import {EXAMPLE_VIEWER_CONTENT_LOADER} from '../../../providers/index';
import {MatTooltip} from '@angular/material/tooltip';
export const CODE_LINE_NUMBER_CLASS_NAME = 'shiki-ln-number';
export const CODE_LINE_CLASS_NAME = 'line';
@@ -44,6 +45,7 @@ export const HIDDEN_CLASS_NAME = 'hidden';
],
templateUrl: './example-viewer.component.html',
styleUrls: ['./example-viewer.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ExampleViewer {
readonly exampleMetadata = input<ExampleMetadata | null>(null, {alias: 'metadata'});
@@ -69,7 +71,6 @@ export class ExampleViewer {
readonly expandable = signal<boolean>(false);
readonly expanded = signal<boolean>(false);
readonly snippetCode = signal<Snippet | undefined>(undefined);
readonly selectedTabIndex = signal<number>(0);
readonly showCode = signal<boolean>(true);
readonly tabs = computed(() =>
this.exampleMetadata()?.files.map((file) => ({
@@ -102,7 +103,10 @@ export class ExampleViewer {
`example-${this.exampleMetadata()?.id.toString()!}`,
);
this.updateExpandable();
const lines = this.getHiddenCodeLines();
const lineNumbers = this.getHiddenCodeLineNumbers();
this.expandable.set(lines.length > 0 || lineNumbers.length > 0);
},
{injector: this.injector},
);
@@ -114,15 +118,6 @@ export class ExampleViewer {
this.setCodeLinesVisibility();
}
toggleCodeVisibility(): void {
const showCode = !this.showCode();
this.showCode.set(showCode);
if (showCode) {
afterNextRender(() => this.setCodeLinesVisibility(), {injector: this.injector});
}
}
copyLink(): void {
// Reconstruct the URL using `origin + pathname` so we drop any pre-existing hash.
const fullUrl =
@@ -135,25 +130,8 @@ export class ExampleViewer {
}
protected onTabIndexChange(index: number): void {
this.selectedTabIndex.set(index);
this.snippetCode.set(this.exampleMetadata()?.files[index]);
afterNextRender(
() => {
this.setCodeLinesVisibility();
this.updateExpandable();
},
{injector: this.injector},
);
}
// A file is expandable when its range hides lines. While the block is expanded nothing is
// hidden, so trust the presence of a range instead of the rendered state.
private updateExpandable(): void {
this.expandable.set(
!!this.snippetCode()?.visibleLinesRange &&
(this.expanded() || this.getHiddenCodeLines().length > 0),
);
this.setCodeLinesVisibility();
}
private getFileExtension(name: string): string {
@@ -212,13 +190,13 @@ export class ExampleViewer {
for (const [index, line] of lines.entries()) {
if (!linesToDisplay.includes(index + 1)) {
line.classList.add(HIDDEN_CLASS_NAME);
} else if (!linesToDisplay.includes(index)) {
} else if (!linesToDisplay.includes(index - 1)) {
appendGapBefore.push(line);
}
}
for (const [index, lineNumber] of lineNumbers.entries()) {
if (!linesToDisplay.includes(index + 1)) {
if (!linesToDisplay.includes(index)) {
lineNumber.classList.add(HIDDEN_CLASS_NAME);
}
}
@@ -22,7 +22,7 @@ import {isExternalLink} from '../../utils/index';
},
})
export class ExternalLink {
private readonly anchor = inject<ElementRef<HTMLAnchorElement>>(ElementRef);
private readonly anchor: ElementRef<HTMLAnchorElement> = inject(ElementRef);
private readonly platformId = inject(PLATFORM_ID);
target?: '_blank' | '_self' | '_parent' | '_top' | '';
@@ -23,7 +23,7 @@ export class SearchItem implements Highlightable {
readonly item = input<SearchResultItem | undefined>();
private readonly elementRef = inject<ElementRef<HTMLLIElement>>(ElementRef);
private readonly elementRef = inject(ElementRef<HTMLLIElement>);
private _isActive = signal(false);
+3
View File
@@ -0,0 +1,3 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0.04145 0.04432l6.56351 8.77603L0 15.95564h1.48651l5.78263-6.24705 4.6722 6.24705h5.05865l-6.9328-9.26967L16.21504.04432h-1.48651l-5.32552 5.75341L5.1001.04432H.04145Zm2.18602 1.09497h2.32396l10.26221 13.72122h-2.32396L2.22747 1.13928Z" fill="#A39FA9"/>
</svg>

After

Width:  |  Height:  |  Size: 367 B

@@ -17,7 +17,6 @@ export interface NavigationItem {
contentPath?: string;
status?: 'new' | 'updated';
category?: string;
categoriesStatus?: Record<string, 'new' | 'updated'>[];
isCrossReferenced?: boolean;
preserveOtherCategoryOrder?: boolean; // true by default
}
+12 -12
View File
@@ -2,26 +2,26 @@
"name": "@angular/docs",
"version": "0.0.0-PLACEHOLDER",
"peerDependencies": {
"@angular/cdk": "^22.2.0-next",
"@angular/common": "^22.2.0-next",
"@angular/core": "^22.2.0-next",
"@angular/forms": "^22.2.0-next",
"@angular/material": "^22.2.0-next",
"@angular/platform-browser": "^22.2.0-next",
"@angular/router": "^22.2.0-next",
"@angular/ssr": "^22.2.0-next",
"@angular/cdk": "^21.2.0-next",
"@angular/common": "^21.2.0-next",
"@angular/core": "^21.2.0-next",
"@angular/forms": "^21.2.0-next",
"@angular/material": "^21.2.0-next",
"@angular/platform-browser": "^21.2.0-next",
"@angular/router": "^21.2.0-next",
"@angular/ssr": "^21.2.0-next",
"algoliasearch": "^5.0.0",
"rxjs": "^7.8.1"
},
"dependencies": {
"@webcontainer/api": "^1.1.8",
"diff": "~9.0.0",
"diff": "~8.0.0",
"emoji-regex": "~10.6.0",
"fflate": "^0.8.2",
"jsdom": "~30.0.0",
"marked": "~18.0.0",
"jsdom": "~28.0.0",
"marked": "~17.0.0",
"mermaid": "^11.0.0",
"shiki": "^4.0.0",
"shiki": "^3.0.0",
"tinyglobby": "^0.2.12"
},
"exports": {
+3 -14
View File
@@ -1,5 +1,3 @@
load("@aspect_rules_js//js:providers.bzl", "js_info")
def _generate_nav_items(ctx):
"""Implementation of the navigation items data generator rule"""
@@ -35,18 +33,9 @@ def _generate_nav_items(ctx):
},
)
# The return value describes what the rule is producing. We expose the generated JSON via
# both DefaultInfo and JsInfo so it can be consumed as a `ts_project` dependency (the new
# rules_angular `ts_project` requires deps to provide JsInfo).
outputs = depset([json_output])
return [
DefaultInfo(files = outputs),
js_info(
target = ctx.label,
sources = outputs,
transitive_sources = outputs,
),
]
# The return value describes what the rule is producing. In this case we need to specify
# the "DefaultInfo" with the output json file.
return [DefaultInfo(files = depset([json_output]))]
generate_nav_items = rule(
# Point to the starlark function that will execute for this rule.
@@ -66,7 +66,7 @@ function getTagSinceVersion(
}
// In case of deprecated tag we need to separate the version from the deprecation message.
const version = tag.comment.match(/^\s*(?:(?:since|from|as of)\s+)?v?(\d+(?:\.\d+)?)/i)?.[1];
const version = tag.comment.match(/\d+(\.\d+)?/)?.[0];
return {version};
}
@@ -1,54 +0,0 @@
/**
* @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
*/
/**
* Flat set of all routes (and `route#anchor` pairs) defined by the application's navigation
* tree. Populated once per pipeline invocation from `defined-routes.json`, which is produced
* by `//adev/scripts/routes:run_generate_route` from a TypeScript import of the canonical
* `ALL_ITEMS` navigation entries.
*
* This is the same source of truth used by the markdown -> HTML guide rendering pipeline
* (see `AdevDocsRenderer#isKnownRoute`), so api-gen and guide-gen agree on what counts as a
* valid link target.
*
* Used by the `{@link /guide/...}` and `[label](/guide/...)` validators to catch broken or
* stale guide links at build time.
*/
let definedRoutes = new Set<string>();
export function setDefinedRoutes(routes: string[]): void {
definedRoutes = new Set(routes);
}
/**
* Whether the given route (e.g. `guide/signals` or `guide/signals#what-are-signals`) is one
* of the known navigation routes.
*/
export function isKnownRoute(route: string): boolean {
return definedRoutes.has(route);
}
/** Whether a defined-routes manifest has been loaded for this pipeline invocation. */
export function hasDefinedRoutes(): boolean {
return definedRoutes.size > 0;
}
/**
* Returns the heading anchor IDs defined for the given route path. Used to suggest a
* corrected anchor when a `#fragment` doesn't match (case-insensitive lookup).
*/
export function getAnchorsForRoute(route: string): string[] {
const prefix = `${route}#`;
const anchors: string[] = [];
for (const currentRoute of definedRoutes) {
if (currentRoute.startsWith(prefix)) {
anchors.push(currentRoute.slice(prefix.length));
}
}
return anchors;
}
@@ -191,7 +191,7 @@ export function getTagSinceVersion<T extends HasJsDocTags>(
}
// In case of deprecated tag we need to separate the version from the deprecation message.
const version = tag.comment.match(/^\s*(?:(?:since|from|as of)\s+)?v?(\d+(?:\.\d+)?)/i)?.[1];
const version = tag.comment.match(/\d+(\.\d+)?/)?.[0];
return {version};
}
@@ -21,6 +21,7 @@ import {
ParameterEntry,
PipeEntry,
TypeAliasEntry,
EntryType,
} from '../entities.mjs';
import {CliCommand, CliOption} from '../cli-entities.mjs';
@@ -104,7 +105,6 @@ export type FunctionEntryRenderable = FunctionEntry &
export type FunctionSignatureMetadataRenderable = FunctionSignatureMetadata &
DocEntryRenderable & {
params: ParameterEntryRenderable[];
htmlReturnDescription?: string;
};
/** Documentation entity for a block augmented with transformed content for rendering. */
@@ -149,7 +149,6 @@ export interface LinkEntryRenderable {
label: string;
url: string;
title?: string;
target?: string;
}
export type CliOptionRenderable = CliOption & {
@@ -13,8 +13,7 @@ import {DocEntry} from './entities.mjs';
import {isCliEntry, isHiddenEntry} from './entities/categorization.mjs';
import {getRenderable} from './processing.mjs';
import {renderEntry} from './rendering.mjs';
import {setCurrentSymbol, setSymbolMembers, setSymbols} from './symbol-context.mjs';
import {setDefinedRoutes} from './defined-routes-context.mjs';
import {setCurrentSymbol, setSymbols} from './symbol-context.mjs';
import {CliCommandRenderable, DocEntryRenderable} from './entities/renderables.mjs';
import {initHighlighter} from '../../shared/shiki.mjs';
import {setHighlighterInstance} from './shiki/shiki.mjs';
@@ -95,35 +94,6 @@ function getNormalizedFilename(normalizedModuleName: string, entry: DocEntry | C
return `${normalizedModuleName}_${entry.name}_${entry.entryType.toLowerCase()}.html`;
}
/**
* Build an index of `<symbol> -> Set<member name>` from a package's entries. Used to validate
* `#member` fragments in `{@link /api/<module>/<Symbol>#<member>}` tags at build time.
*
* Walks any entry that has a `members` array (classes, interfaces, enums, decorators, namespaces,
* directives, pipes), without committing to a particular concrete entry shape.
*/
function buildSymbolMembersIndex(entries: (DocEntry | CliCommand)[]): Map<string, Set<string>> {
const index = new Map<string, Set<string>>();
for (const entry of entries) {
const members = (entry as {members?: {name: string}[]}).members;
if (!Array.isArray(members) || members.length === 0) {
continue;
}
const name = (entry as {name?: string}).name;
if (!name) {
continue;
}
const set = index.get(name) ?? new Set<string>();
for (const member of members) {
if (member?.name) {
set.add(member.name);
}
}
index.set(name, set);
}
return index;
}
async function main() {
// Shiki highlighter needs to be setup in an async context
setHighlighterInstance(await initHighlighter());
@@ -131,9 +101,7 @@ async function main() {
const [paramFilePath] = process.argv.slice(2);
const rawParamLines = readFileSync(paramFilePath, {encoding: 'utf8'}).split('\n');
const [srcs, outputFilenameExecRootRelativePath, definedRoutesPath] = rawParamLines;
setDefinedRoutes(JSON.parse(readFileSync(definedRoutesPath, {encoding: 'utf8'})) as string[]);
const [srcs, outputFilenameExecRootRelativePath] = rawParamLines;
// Docs rendering happens in three phases that occur here:
// 1) Aggregate all the raw extracted doc info.
@@ -152,7 +120,6 @@ async function main() {
// Setting the symbols are a global context for the rendering templates of this entry
setSymbols(collection.symbols);
setSymbolMembers(buildSymbolMembersIndex(collection.entries));
const renderableEntries: (DocEntryRenderable | CliCommandRenderable)[] = [];
for (const entry of extractedEntries) {
@@ -18,15 +18,9 @@ def _render_api_to_html(ctx):
args.add(html_output_directory.path)
outputs = [html_output_directory]
# Path to the defined-routes manifest used to validate guide links in JSDoc and markdown.
# The manifest is a flat JSON array of route paths and `route#anchor` entries derived from
# the application's navigation tree.
args.add(ctx.file.defined_routes.path)
inputs = ctx.files.srcs + [ctx.file.defined_routes]
# Define an action that runs the executable.
ctx.actions.run(
inputs = depset(inputs),
inputs = depset(ctx.files.srcs),
executable = ctx.executable._render_api_to_html,
outputs = outputs,
arguments = [args],
@@ -51,13 +45,6 @@ render_api_to_html = rule(
# The source files for this rule. This must include one or more json data files.
"srcs": attr.label_list(allow_empty = False, allow_files = True),
# Defined-routes manifest produced by //adev/scripts/routes:run_generate_route. Used
# to validate `/guide/...` URLs in `{@link}` JSDoc tags and markdown links.
"defined_routes": attr.label(
default = Label("//adev/scripts/routes:defined-routes.json"),
allow_single_file = [".json"],
),
# The executable for this rule (private).
"_render_api_to_html": attr.label(
default = Label("//adev/shared-docs/pipeline/api-gen/rendering:render_api_to_html"),
@@ -34,17 +34,16 @@ export function replaceKeywordFromShikiHtml(
shikiHtml: string,
replaceWith: string,
): string {
const keywordElement = `<[^>]*>(?:${keyword})<\\/\\w+>`;
return (
shikiHtml
// Linking an identifier causes Shiki to emit the space before it as a separate element.
.replace(new RegExp(`(${keywordElement})<[^>]*>\\s+<\\/\\w+>`, 'g'), '$1')
// Without a link, Shiki includes the space in the identifier's element instead.
.replace(new RegExp(`(${keywordElement}<[^>]*>)\\s+(?=[^<\\s]+<\\/\\w+>)`, 'g'), '$1')
// remove the leading space of the element after the keyword
.replace(
new RegExp(`(<[^>]*>(?:${keyword})<\\/\\w+><[^>]*>)(\\s)(\\w+<\\/\\w+>)`, 'g'),
'$1$3',
)
// Shiki requires the keywords (eg. function,interface) for highlighting signatures
// here we are replacing it
.replace(new RegExp(keywordElement, 'g'), replaceWith)
.replace(new RegExp(`<[^>]*>(?:${keyword})<\\/\\w+>`, 'g'), replaceWith)
);
}

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