Compare commits

..

211 Commits

Author SHA1 Message Date
Leon Senft 70668befde release: cut the v21.1.3 release 2026-02-04 16:06:24 -08:00
Matthieu Riegler 767c6b4754 fix(vscode-extension): support highlighting for class bindings with brackets
Tailwind classes can often be quite complex strings. This change adds supports for classes with backets.

fixes #66818

(cherry picked from commit 01ed57f297)
2026-02-04 15:45:08 -08:00
SkyZeroZx 1bf6bfc07f test(forms): remove zone-based testing utilities
Removes usages of zone-based helpers such as `fakeAsync` , `tick`
`waitForAsync` as part of the migration to zoneless tests.

Completes the transition to zoneless.

(cherry picked from commit 7dfbacbcf8)
2026-02-04 15:38:13 -08:00
Walter Breakell 70313f13fe docs: fix syntax errors and improve grammar in navigation guide
(cherry picked from commit d6585fe837)
2026-02-04 15:37:21 -08:00
Jef f9b69cc67b docs: fix typo
Add missing n to componet(s)

(cherry picked from commit 81876c2583)
2026-02-04 15:36:52 -08:00
Ben Hong 52dfcd9865 docs: add guidance on submit and novalidate for signal forms
(cherry picked from commit 38428945ac)
2026-02-04 15:36:22 -08:00
Angular Robot d793dde00f build: update cross-repo angular dependencies
See associated pull request for more information.
2026-02-04 15:30:54 -08:00
Kristiyan Kostadinov 04d518bc26 refactor(forms): work around internal issue
Works around an internal property renaming issue by changing how we declare the interface for `InteropNgControl`.

(cherry picked from commit 680d99b1c3)
2026-02-04 14:25:48 -08:00
David Neil 2cf4da0ea1 fix(core): hold constructors weakly in DepsTracker cache
Otherwise a component can be held in memory longer than
the component constructor is reachable from application code.

(cherry picked from commit cab5ddd526)
2026-02-03 12:44:27 -08:00
Matthieu Riegler f15d33d68b docs(docs-infra): extract call signature return type.
fixes #66799

(cherry picked from commit 43d61ecbb2)
2026-02-03 12:31:40 -08:00
Shuaib Hasan Akib 33e8cde034 docs: generalize wording for CSP nonce configuration options
Replaces “one of two ways” with “one of the following ways” to avoid
hardcoding the number of supported approaches and keep the
documentation accurate if options change.

fixes: #66861
(cherry picked from commit cfa7664361)
2026-02-03 12:27:15 -08:00
Angular Robot 8343b05ad7 build: lock file maintenance
See associated pull request for more information.
2026-02-03 12:26:06 -08:00
Matthieu Riegler 443654ac77 docs(docs-infra): fix foot width
This was due to a regression introduced by #66825

fixes #66880

(cherry picked from commit 9c032972a8)
2026-02-03 12:25:46 -08:00
David Neil 49a36f4cc7 perf(router): Use .bind to avoid holding other closures in memory
In many JS runtimes all closures created in the same scope share a context
this means that data held in one of the closures is not collected until all of the closures are collected.
This change prevents the returned promise from holding a reaction that holds the entire `Router` object in memory.

(cherry picked from commit 3867cd8554)
2026-02-03 12:25:02 -08:00
Angular Robot 6505a26ee0 docs: update cross-repo adev docs
Updated Angular adev cross repo docs files.
2026-02-02 15:11:22 -08:00
Shahar Har-Shuv 96095f435e docs: Rename Field directive -> FormField in form-logic.md
(cherry picked from commit f29fcd882a)
2026-02-02 14:41:03 -08:00
Alex Rickabaugh 2b254bc050 fix(core): linkedSignal.update should propagate errors
Unlike a normal `signal()`, a `linkedSignal()` can be in an error state when
its computation fails. Currently, there's a bug where `linkedSignal.update`
does not account for this error state, and will pass the internal `ERRORED`
`Symbol` as the current value to the updater function.

This commit fixes the issue by having `update()` check and throw the error
instead of calling the updater function.

(cherry picked from commit aff9e36a98)
2026-02-02 12:21:36 -08:00
Kristiyan Kostadinov e5110b4fa1 fix(core): export DirectiveWithBindings
Exports the `DirectiveWithBindings` interface since it's part of the public API of `createComponent`.

Fixes #66851.

(cherry picked from commit 8ab433abdd)
2026-02-02 11:08:45 -08:00
SkyZeroZx f1e4446071 feat(docs-infra): Implements select component using Angular Aria
Replaces the native select element with a custom combobox implementation for improved accessibility and styling.

(cherry picked from commit 70615117a2)
2026-02-02 11:07:58 -08:00
AleksanderBodurri 79519409cd feat(devtools): promote router tree to stable
Previous the router tree was an opt-in feature that required manual enablement in settings.

Now the router tree is enabled by default whenever the application supports it and routes are detected.

(cherry picked from commit 35046d3dba)
2026-02-02 11:06:45 -08:00
SkyZeroZx 4729e804ac docs(docs-infra): Fix border color issue on aria select when closing on outside click
Prevents incorrect border color from appearing on close triggered by an outside click

(cherry picked from commit cdbb957334)
2026-02-02 11:04:48 -08:00
dominicbachmann 575f8a39c7 docs(animations): fix small typo in animations migrations docs
(cherry picked from commit a64a90dbbe)
2026-02-02 10:18:10 -08:00
Jaime Burgos f80e6c2f77 docs: remove component suffix router guide
(cherry picked from commit bd27aa2cf3)
2026-02-02 10:17:41 -08:00
SkyZeroZx bbd5cf82b1 test(forms): migrate reactive forms and value accessors to zoneless
Migrates reactive forms, template-driven forms, and value accessors to zoneless tests.

(cherry picked from commit 4aae3379c2)
2026-02-02 10:17:03 -08:00
SkyZeroZx c64db1d0f3 docs: update llms.txt
(cherry picked from commit 825122b6d2)
2026-02-02 10:14:07 -08:00
kirjs 5301711b4e docs: rename agents.md to AGENTS.md
(cherry picked from commit 19aa1eb95e)
2026-02-02 10:10:45 -08:00
kirjs 92ac00d507 docs: add PR section to agents.md
(cherry picked from commit a8d0155303)
2026-02-02 10:10:45 -08:00
kirjs fdd7a4deb5 docs: add agent configuration guide
Adds agents.md to the root directory to provide context and instructions for AI agents working in this repository.

(cherry picked from commit 9306abe883)
2026-02-02 10:10:45 -08:00
SkyZeroZx df7bf134fe docs(docs-infra): ensure code preview copy preserves spaces
Preserve whitespace when copying code examples from the docs. Fixes the copy handler so copied snippets keep original spacing.

Fixes #66790

(cherry picked from commit 522c5ba868)
2026-02-02 10:09:39 -08:00
Jaime Burgos 665b33ecdc docs: update zoneless guide
(cherry picked from commit 3d09d8e831)
2026-02-02 10:05:26 -08:00
Angular Robot eb2803237b build: update cross-repo angular dependencies
See associated pull request for more information.
2026-02-02 09:59:35 -08:00
SkyZeroZx 6f75b6e3f6 fix(forms): Resolves debounce promise on abort in debounceForDuration
Ensures the promise returned by the debouncer resolves
when aborted, preventing potential hangs for awaiting consumers.
Fixes #66646

(cherry picked from commit b1bf535f8e)
2026-01-30 09:19:47 -08:00
Charles Lyding efb64bf9fd build(language-service): remove local development build script
The standalone build.sh script for the language service is no longer
required as the vscode-ng-language-service repository has been merged
into this monorepo. Local development and testing of the extension
can now be handled via the integrated build system.

(cherry picked from commit f9bcccc2b9)
2026-01-29 13:16:53 -08:00
Matthieu Riegler e0a260a425 docs(docs-infra): wrap getTextOfJSDocComment
This commits adds a wrapper around `ts.getTextOfJSDocComment` because of bugs that won't be fixed by the TS team (see microsoft/TypeScript#63027)

(cherry picked from commit 72dfb4d92a)
2026-01-29 13:16:05 -08:00
Angular Robot 8dfbb8b5d5 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-01-29 12:30:49 -08:00
Angular Robot 3845f7e930 build: update github/codeql-action action to v4.32.0
See associated pull request for more information.
2026-01-29 12:29:18 -08:00
Shuaib Hasan Akib cc9a58a2ae feat(docs-infra): increase table-of-contents width for large screens
Add media query overrides to expand the table-of-contents width on extra-large desktops.

fixes: #66823
(cherry picked from commit 6990f88d97)
2026-01-29 12:24:04 -08:00
Angular Robot e3681b7b5f build: update dependency tar.bzl to v0.8.1
See associated pull request for more information.
2026-01-29 12:23:31 -08:00
Jessica Janiuk 70a5b651be fix(core): prevent element duplication with dynamic components
When dynamic components are rapidly added and removed with animate.leave and animate.enter, a leave animation might fire before the enter animation could, causing an element to be retained. This fix prevents that from occuring by clearing the enter animations in this case.

fixes: #66794
(cherry picked from commit c66a19f0de)
2026-01-29 12:18:05 -08:00
Charles Lyding 4c7126d23b fix(localize): add support for unit-test builder in ng-add schematic
This commit updates the @angular/localize ng-add schematic to support
the @angular/build:unit-test builder. It ensures that @angular/localize
is added to the types array in the TypeScript configuration file
associated with the unit-test target. If no tsConfig is specified in
the target options, it defaults to tsconfig.spec.json in the project root.

(cherry picked from commit 1c3b1cf18d)
2026-01-29 12:17:19 -08:00
Andrew Scott d6268c0bbb fix(router): limit UrlParser recursion depth to prevent stack overflow
Deeply nested parentheses in URLs (e.g. `(a/(b/(c...)))`) trigger recursive calls in `UrlParser`, which can lead to a `RangeError: Maximum call stack size exceeded`. While such errors are generally caught by the framework, relying on the runtime's stack limit is unpredictable across different environments and engine states (e.g. varying stack sizes in different browsers or Node.js versions).

The deeply nested parentheses  can cause a stack overflow. While such URLs can be valid (e.g., `(a/(b/(c...)))`) and serialize to simple paths (e.g., /a/b/c), excessive nesting is unreasonable and likely malicious or accidental.

Linear paths (e.g. /a/b/c/d) are parsed iteratively and do NOT trigger recursion. Only parentheses trigger recursion.

This commit introduces a recursion depth limit of 50. If parsing exceeds this depth, the router will now throw a specific `UNPARSABLE_URL` error with the message "URL is too deep". This ensures a deterministic failure mode that is easier for applications to handle than a crash or generic RangeError.

The limit of 50 is chosen as it should accommodate any reasonable application URL structure (including complex named outlets) while providing a safe upper bound against abusive payloads.

This is essentially a refactor of the error state:

* Before: RangeError (System says "I'm out of stack memory")
* After: RuntimeError (Validator says "Input is invalid")

This provides:

* Semantic Correctness: The error now correctly blames the input ("URL too deep"), not the environment ("Stack full").
* Cross-Platform Consistency: The limit is the same in Chrome, Firefox, Node, and Deno, regardless of their internal recursion limits.
* Fast Failure: We stop at depth 50 instead of depth ~15,000, saving those cycles (though CPU cost is negligible either way).

"wide" URLs are now theoretically more expensive than "deep" URLs (because deep ones fail fast), but both are well within safe bounds for any reasonable input size.

(cherry picked from commit 458bc4a2c8)
2026-01-29 12:15:25 -08:00
SkyZeroZx 0a4c647ef0 refactor(core): Remove unsued properties in differ factories
Simplifies differ factories by removing unnecessary constructors and properties.

Also removes the formatError function as it is no longer used.

(cherry picked from commit 0c2efc0d46)
2026-01-29 12:06:37 -08:00
JakobDev b9b934363a docs: Add missing link in HostListener documentation
(cherry picked from commit 0e04233a7c)
2026-01-29 11:56:48 -08:00
SkyZeroZx cfe7340270 refactor(core): remove unused restriction parameter
Removes the `restriction` parameter from `registerAppScopedDispatcher` and `registerGlobalDispatcher`.

(cherry picked from commit d072791f13)
2026-01-28 20:54:49 +00:00
Andrew Scott 4f0d29d92a release: cut the v21.1.2 release 2026-01-28 20:36:08 +00:00
Angular Robot 290b2814e1 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-01-28 19:28:34 +00:00
Leon Senft 9f99b14882 fix(forms): only touch visible, interactive fields on submit
Don't touch hidden, disabled, or readonly fields on submit, since they
don't contribute to form validity. This also prevents errors from
appearing immediately if they're later made interactive.

Fix #66344

(cherry picked from commit e682e53113)
2026-01-28 18:56:06 +00:00
Angular Robot 3de87e1eda build: update pnpm to v10.28.2
See associated pull request for more information.
2026-01-28 18:36:21 +00:00
Angular Robot affd2362eb build: lock file maintenance
See associated pull request for more information.
2026-01-28 18:27:17 +00:00
Angular Robot 11657fb0b1 docs: update cross-repo adev docs
Updated Angular adev cross repo docs files.
2026-01-28 18:23:34 +00:00
Matthieu Riegler 7242da24fb docs: reword docs on standalone.
fixes #66773

(cherry picked from commit 611f6a49d1)
2026-01-28 18:16:58 +00:00
Leon Senft 31e6f0d7dc test(forms): submit behavior while validation is pending
Ensure `submit()` behaves as expected while a form is pending.

- Submission is not blocked by pending validation.
- Submission errors prevent pending validation errors from appearing
  after they resolve on the same field.
- Submission errors don't prevent pending validation errors from
  appearing after they resolve on subfields.

(cherry picked from commit 01bfb83fc9)
2026-01-28 00:15:33 +00:00
Jaime Burgos 6601f06e44 test(common): enables zoneless change detection in tests
Adds `provideZonelessChangeDetection` to TestBed configurations in  `ngComponentOutlet` , `ngOptimizedImage` , `ngTemplateOutlet` and `ngPlural`.

(cherry picked from commit 4448356313)
2026-01-28 00:08:50 +00:00
Shuaib Hasan Akib fe69342545 docs: fix wrong line highlights
Corrects incorrect line highlighting in code examples so the highlighted
lines match the intended sections being explained.

(cherry picked from commit 160a8b4be7)
2026-01-26 23:56:45 +00:00
Andrew Scott c57b0355b5 fix(language-service): Detect local project version on creation
This updates the language service to use the detected version of angular
core in the given project on load rather than the minimum detected
version in the workspace

(cherry picked from commit 8a7cbd4668)
2026-01-26 23:51:34 +00:00
Matthieu Riegler 06952b4f0f docs(docs-infra): remove unused examples
(cherry picked from commit 899df39c7c)
2026-01-26 23:19:39 +00:00
Shuaib Hasan Akib c133a2c1eb docs: normalize spacing in animation example boxes
Simplifies box padding and removes default paragraph margins to ensure consistent spacing and alignment
in the animation examples.

(cherry picked from commit ceefceb004)
2026-01-26 22:59:32 +00:00
SkyZeroZx 3151bc38a0 docs: Updates form documentation (#66732)
Updates the form documentation to link to the API reference for the `form()` function.

Also removes unused `HttpClient` import from the async validation example.

PR Close #66732
2026-01-26 22:46:12 +00:00
SkyZeroZx 49e3c25d62 docs(docs-infra): Exempts more symbols from automatic linking (#66732)
Extends the list of symbols that should not be
automatically linked for forms.

PR Close #66732
2026-01-26 22:46:11 +00:00
Shuaib Hasan Akib b4227fb3f1 docs: apply prefer/avoid blocks in UI with comments
Apply inline comments with proper Prefer and Avoid UI blocks so
guidance is more visible and consistent across the documentation.

(cherry picked from commit 8efee1c71d)
2026-01-26 22:33:15 +00:00
Matthieu Riegler 89525ec6b7 docs: add mentions of the global target syntax.
(cherry picked from commit 87a72f3dd0)
2026-01-26 22:31:14 +00:00
Georgi Serev 1234e873d8 docs(docs-infra): update the look of api items state labels
Update how dev preview, experimental and deprecated labels are visualized in the API list to avoid ambiguity with the current design on larger screens.

(cherry picked from commit 9625780259)
2026-01-26 22:30:22 +00:00
SkyZeroZx 359470979b refactor(core): remove index from text node creation
Removes unnecessary `index` parameter from `_locateOrCreateTextNode`
and `locateOrCreateTextNodeImpl` functions.

(cherry picked from commit 1df564b9f9)
2026-01-26 22:22:59 +00:00
Shuaib Hasan Akib 27c7b60d81 docs: fix wrong line highlight in lightweight injection token for API definition example
(cherry picked from commit ca9b5bd658)
2026-01-26 22:22:04 +00:00
huangkevin-apr 72ed76feb3 docs(docs-infra): improve accessibility of search dialog trigger
- Add explicit aria-label for screen reader users
- Preserve title tooltip for mouse users

(cherry picked from commit b3f2bad593)
2026-01-26 22:21:40 +00:00
Damian Sire 278cfd3879 docs: fix Genkit documentation URL
Update the Genkit Angular integration documentation link from the old path
/docs/angular/ to the current correct path /docs/frameworks/angular/

This ensures developers are directed to the correct and up-to-date
documentation for integrating Genkit with Angular applications.

(cherry picked from commit ffe40f4bfa)
2026-01-26 22:20:17 +00:00
Modeste ASSIONGBON 5903bf7c98 docs: fix typo in designing-your-form-model.md
"A form model model" -> duplicated word
"to save bytes me might want" -> "we" instead of "me"

(cherry picked from commit 19b59df2dc)
2026-01-26 22:19:54 +00:00
SkyZeroZx 051ba061a0 refactor(core): improve tree-shaking
Refactors getNodeInjectable to avoid pulling in stringifyForError production builds, reducing unnecessary symbols in production bundles

(cherry picked from commit 8feb541060)
2026-01-26 22:19:00 +00:00
AleksanderBodurri 01b035ab03 feat(devtools): mark special element injector providers as internal
Differentiates special providers like ElementRef from other providers in the DevTools UI.

(cherry picked from commit 105274fe32)
2026-01-26 22:18:06 +00:00
Ben Hong 4742c703df docs: add signal forms - form logic guide
Co-authored-by: Leon Senft <leonsenft@users.noreply.github.com>
(cherry picked from commit 1d1777931e)
2026-01-26 22:14:01 +00:00
Andrew Scott 21ecdc036a fix(router): Do not intercept reload events with Navigation integration
This commit prevents the Router from intercepting reload navigations
in the navigate event listener. This would convert hard page reloads
to SPA navigations.

fixes #66746
2026-01-26 20:58:49 +00:00
Angular Robot 2499238c59 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-01-23 20:05:38 +00:00
SkyZeroZx e6ea328cf4 docs: update bootstrapApplication docs
(cherry picked from commit 85122cb12d)
2026-01-23 11:45:20 -08:00
Angular Robot b203ab0881 build: update bazel dependencies
See associated pull request for more information.
2026-01-23 18:43:23 +00:00
Matthieu Riegler f7eb996ad5 docs: update signal form tutorials to use FormField
(cherry picked from commit ddd3198dc2)
2026-01-22 17:02:28 -08:00
Angular Robot d9e7ab3db6 build: update all github actions
See associated pull request for more information.
2026-01-23 00:36:13 +00:00
SkyZeroZx 458e21e0fe docs(docs-infra): Exempts more symbols from automatic linking
Extends the list of symbols that should not be
automatically linked.

(cherry picked from commit 30b50e7142)
2026-01-22 15:01:43 -08:00
Matthieu Riegler e4c9ff7a34 build: Update prettier to 3.8
This version adds support for `angular-html` and `angular-ts` formatting in our markdown files.

(cherry picked from commit 145e77d09d)
2026-01-22 21:13:21 +00:00
Kristiyan Kostadinov c9043fb876 refactor(compiler-cli): remove deprecated signature usage
The signature for `createImportClause` was deprecated in TS 5.9. These changes switch to the non-deprecated one.

(cherry picked from commit dbc1452de3)
2026-01-22 21:02:42 +00:00
Angular Robot 61dbdee6fb build: update cross-repo angular dependencies
See associated pull request for more information.
2026-01-22 19:46:02 +00:00
Andrew Scott 27eeff9be6 release: bump VSCode extension version to 21.1.1 (#66714) 2026-01-22 10:31:57 -08:00
Alon Mishne f577f8cb5c release: cut the v21.1.1 release 2026-01-21 21:58:24 +00:00
Angular Robot 643f0f4b22 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-01-21 12:43:47 -08:00
Matthieu Riegler ad9e473e73 docs(docs-infra): fix regression on docs-tabs
(cherry picked from commit 4ca2722d6f)
2026-01-21 20:43:07 +00:00
Matthieu Riegler 18842266de docs(docs-infra): Handle additional description format
Ex: https://angular.dev/api/router/withExperimentalPlatformNavigation
(cherry picked from commit f0b1061791)
2026-01-21 19:37:11 +00:00
Matthieu Riegler 223ff74891 build: don't substitute binary files
Previously, ng_package applied version stamping to all source files, which corrupted binary files like SQLite databases.

This change ignores text substitution for files passed to the `data` attribute, ensuring binary assets are preserved intact in the final package.

fixes #66637

(cherry picked from commit b885851cbe)
2026-01-21 18:35:08 +00:00
JoostK 0e1f1ed573 fix(compiler-cli): drop .tsx extension for generated relative imports
When synthesizing an import corresponding with a .tsx file, the extension
would not be removed unlike regular .ts files. Adjust the import generators
to also drop any .tsx extension from module specifiers.

Closes #66262

(cherry picked from commit d94b19a92f)
2026-01-21 18:33:17 +00:00
aparziale b9c995e7d3 refactor: Delete counterproductive aria-label
Delete counterproductive aria-label in ADEV

fixes #66604

(cherry picked from commit 0fa8099d50)
2026-01-21 18:25:38 +00:00
Shuaib Hasan Akib 0d0a86be29 docs: cleanup copy button and $ from output
(cherry picked from commit 1f86de019f)
2026-01-21 18:24:21 +00:00
Angular Robot 8078739e62 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-01-21 10:22:39 -08:00
Angular Robot 60aceefc75 build: update pnpm to v10.28.1
See associated pull request for more information.
2026-01-21 10:19:09 -08:00
Andrew Scott 696e827391 refactor(language-server): split session into separate handlers
Breaks up the large session.ts file into separate handlers and consolidates the code related to completions in the server into the handler file
2026-01-21 10:12:47 -08:00
Kristiyan Kostadinov 6a5620442f fix(docs-infra): use focus-visible for focus styling
`focus-visible` should lead to a better experience, compared to showing the state on any focus.

(cherry picked from commit b275206a49)
2026-01-21 01:17:06 +00:00
Shuaib Hasan Akib 62d847325d refactor(common): cleanup unused code in doc regions
Cleans unused imports and code
that is not referenced within the associated docregion blocks.

(cherry picked from commit 6284126da5)
2026-01-21 00:31:13 +00:00
Kristiyan Kostadinov 3c8d69794c fix(docs-infra): don't use private symbol
Fixes that the tab group was referring to a private symbol from the CDK.

(cherry picked from commit 94a8f797e5)
2026-01-21 00:15:22 +00:00
Kristiyan Kostadinov 93f3cca5e9 fix(docs-infra): use components directly
Switches the Material and CDK components to be used directly, instead of using the modules. This allows better diagnostics for unused directives.

(cherry picked from commit 508d9c34d5)
2026-01-21 00:14:49 +00:00
Kristiyan Kostadinov ea70b003b0 refactor(compiler): remove unused symbols
Removes symbols that aren't used anymore from the identifiers map.

(cherry picked from commit e4c09776b0)
2026-01-21 00:14:19 +00:00
Kristiyan Kostadinov 5d1b950324 docs: fix serif font
Fixes some headers that were using a serif font unintentionally.

(cherry picked from commit 094cdfe0b5)
2026-01-21 00:12:55 +00:00
Matthieu Riegler e6642496b0 ci: add template for vscode dedicated issues
We didn't have an entry since we migrated the vscode extension repo to the fw repo.

(cherry picked from commit 75c40e7ac7)
2026-01-20 18:24:00 +00:00
Leon Senft 26c2fe6db2 refactor(forms): remove unused API
Remove `SubmittedStatus` which is no longer used.

(cherry picked from commit d0ba332292)
2026-01-20 18:20:01 +00:00
SkyZeroZx e828f1c765 docs: Update ngComponentOutlet binding and hydration note
(cherry picked from commit 5dd17d7b81)
2026-01-20 18:14:34 +00:00
SkyZeroZx 01e98ded2e docs(docs-infra): : Exempts animation-related symbols from linking
Exempts 'group()', 'keyframes', and '@keyframes' symbols from automatic linking.

(cherry picked from commit 00455105cf)
2026-01-20 18:13:55 +00:00
SkyZeroZx 4a1dcb2d82 docs: remove suffix in elements , templates , components & signals
(cherry picked from commit 6f48b4f5ed)
2026-01-20 18:13:28 +00:00
Shuaib Hasan Akib 58720dbc56 refactor(forms): update Reactive Forms guide URL
Updates the Reactive Forms documentation link to the new `guide/forms/reactive-forms` path after the recent docs restructure.

(cherry picked from commit 4adbc4fa19)
2026-01-20 18:11:51 +00:00
Angular Robot 9cc0f4d003 docs: update cross-repo adev docs
Updated Angular adev cross repo docs files.
2026-01-20 10:03:37 -08:00
Joey Perrott 82593de5a4 fix(docs-infra): support providing links to multiple symbols within a single line of a code block
Previously our system only found the first matching symboling to link to within a code block on each line, now we set up a link for all of the discovered symbols on each line

Fixes #65403

(cherry picked from commit a88f353a45)
2026-01-20 17:35:36 +00:00
omar-almasry11 068bb9d3c5 fix(docs-infra): improve skip-to-main-content method to focus <main> element instead of h1
Update the skip-to-main-content behavior to focus the <main> element when present, with a fallback to the first heading for legacy layouts without a main landmark.

(cherry picked from commit 30b78bdc71)
2026-01-20 17:35:01 +00:00
Damian Sire eb6af58446 docs: fix spelling errors in documentation
Fix 6 spelling errors across documentation files:
- components-scenarios.md: 'recieved' → 'received', 'stubbe' → 'stubbed', 'controle' → 'control'
- overview.md: 'dependant' → 'dependent'
- creating-libraries.md: 'to to' → 'to'
- enter-and-leave.md: 'a an' → 'an'

(cherry picked from commit 6dff1cf85d)
2026-01-20 17:32:31 +00:00
Matthieu Riegler 760e2aaa79 docs: Add mention of "documentation"
This should help to bring adev at the top when searching for "angular documentation" or "angular docs"

(cherry picked from commit 2080dd1fec)
2026-01-20 17:31:42 +00:00
Maciej Ślusarz 85cf7f0daa docs: Fix typo in image loader step of Optimizing Images chapter
(cherry picked from commit 8b9bdd0a63)
2026-01-20 17:30:46 +00:00
Angular Robot f0bcf876ec build: lock file maintenance
See associated pull request for more information.
2026-01-20 09:28:39 -08:00
Jessica Janiuk 6b34e5f444 docs: Add section on animate.leave for clarity
There has been a bunch of confusion as to how `animate.leave` works on child nodes when the non-animated parent is removed. This section addition should clarify that.

(cherry picked from commit 5d46554e3f)
2026-01-16 21:26:48 +00:00
Angular Robot ac50dbc8e1 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-01-16 10:25:28 -08:00
Kristiyan Kostadinov 32ad1e09b4 build: move zone.js build off deprecated flag
The Zone.js build was depending on the `--outFile` flag from TypeScript which is deprecated. These changes switch to using `--outDir` and copying the files out of the directory instead.

(cherry picked from commit 52bc0208f9)
2026-01-16 17:28:50 +00:00
Miles Malerba cb75f9ce85 fix(forms): fix control value syncing on touch
Previously we were unconditionally setting the control value back into
the value, regardless of if it had actually been changed. This PR
changes the logic to flush the pending sync on touch if there is one, or
just skip it if there isn't.

(cherry picked from commit e7b2dde6d1)
2026-01-16 17:25:31 +00:00
Kristiyan Kostadinov ded654dc6b build: initial test of TypeScript 6
Resolves some initial test failures after updating to TypeScript 6.

(cherry picked from commit d9c980a958)
2026-01-15 21:41:05 +00:00
Kristiyan Kostadinov 05adfcf8f2 fix(core): handle Set in class bindings
Currently migrating from `[ngClass]` to `[class]` isn't entirely supported, because `[ngClass]` supports `Set` values while `[class]` ignores them.

These changes add a bit of logic to bring them closer together and make the migration easier.

(cherry picked from commit 4831a9f676)
2026-01-15 21:40:02 +00:00
Jeevan Mahesha aa39f87021 docs: update directive name from [field] to [formField] in signal-forms documentation
(cherry picked from commit 417c385ddc)
2026-01-15 19:41:07 +00:00
Alan Agius 0643f541f9 docs: clarify that FESM can depend on shared chunks
Closes #66569

(cherry picked from commit c7eee8300f)
2026-01-15 19:05:45 +00:00
Miles Malerba d89a80a970 feat(forms): Ability to manually register a form field binding in signal forms
This PR adds the ability to manually register a binding with the
`FormField` directive. This is useful for a lower-level implementation
that takes the field tree as an `input()` rather than relying on the
automatic binding from `FormUiControl`.

(cherry picked from commit 5974cd0afc)
2026-01-15 19:03:32 +00:00
Joey Perrott 0f3f346ec4 fix(docs-infra): use public folder instead of assets folder for static files in adev tutorials
Use public folder rather than assets folder after the change in angular-cli's defaults for angular.json

Fixes #58981

(cherry picked from commit f2cf96b519)
2026-01-15 18:59:30 +00:00
Angular Robot 74017eb355 build: update github/codeql-action action to v4.31.10
See associated pull request for more information.
2026-01-15 10:57:40 -08:00
Angular Robot 72877b6c60 build: update bazel dependencies
See associated pull request for more information.
2026-01-15 10:55:45 -08:00
Matthieu Riegler c0446da35f docs(docs-infra): prevent heading from linking symbols
(cherry picked from commit a792315f09)
2026-01-15 16:59:18 +00:00
Angular Robot ae4e2e02d0 build: update cross-repo angular dependencies to v21.1.0
See associated pull request for more information.
2026-01-14 15:05:15 -08:00
Angular Robot 7f9990710e build: update cross-repo angular dependencies
See associated pull request for more information.
2026-01-14 12:21:32 -08:00
Jessica Janiuk 6027c9128c release: cut the v21.1.0 release 2026-01-14 11:44:32 -08:00
Joey Perrott b108ece707 ci: update algolia synonym updater to use node directly rather than tsx
With new version of node we can import ts files directly in our scripts rather than using tsx

(cherry picked from commit 3f13db87e2)
2026-01-14 17:26:43 +00:00
Angular Robot 3cc549c723 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-01-14 08:59:24 -08:00
Angular Robot e817f8c830 docs: update cross-repo adev docs
Updated Angular adev cross repo docs files.
2026-01-14 08:51:44 -08:00
Matthieu Riegler 1ea5c97703 feat(forms): allow focusing bound control from field state
Allows focusing the assocated bound control from the `FieldState`.
2026-01-14 08:26:54 -08:00
SkyZeroZx d81f8ea0f6 docs(docs-infra): Exempts animation-related symbols from linking
Exempts 'animation', 'transition', and 'trigger' symbols from automatic linking.

(cherry picked from commit 724e917f7a)
2026-01-14 16:24:53 +00:00
Shuaib Hasan Akib 654ed6512b docs: add IMPORTANT prefix to pipe usage warning and
Adds an IMPORTANT label to emphasize the guidance about avoiding impure
pipes due to potential performance impact.

(cherry picked from commit c9f584b918)
2026-01-14 16:11:03 +00:00
Jessica Janiuk 134742fb7b Revert "refactor(core): remove ng when invoking enableProdMode"
This reverts commit cb5879f566.

(cherry picked from commit e673eb6df2)
2026-01-14 16:09:02 +00:00
SkyZeroZx 5594a535c1 docs(docs-infra): use Signals Forms focusBoundControl
Removes direct `ViewChild` access from `TextField` components and relies on
Signals Forms via the `focusBoundControl`  to manage focus.

(cherry picked from commit 074666b2e8)
2026-01-14 16:00:49 +00:00
Angular Robot 5097df84b4 build: update dependency node to v22.22.0
See associated pull request for more information.
2026-01-14 07:58:48 -08:00
Angular Robot c4784bd39e build: update dependency bazel to v8.5.1
See associated pull request for more information.
2026-01-14 07:58:21 -08:00
Alan Agius d576ea577d fix(docs-infra): remove trailing slash from sitemap URLs
Fixes an issue where a trailing slash was sometimes added to the end of URLs in the sitemap, causing unnecessary redirects.

Example of problematic URL:
```xml
<loc>https://angular.dev/</loc>
```

(cherry picked from commit 710a4bf02b)
2026-01-14 15:57:13 +00:00
Alan Agius 230b65cc81 build: remove tsx dependency and update release script to use node directly
Node.js can run typescript directly.

(cherry picked from commit 95e8c2fdee)
2026-01-14 15:56:42 +00:00
Andrew Scott 6d3b09326a build: Update vscode release script
- Ensure there is a GITHUB_TOKEN environment variable at the start so we can push the release
- More robust handling for finding releaser's fork if it's not 'origin'

(cherry picked from commit 227acddd3a)
2026-01-13 21:08:30 +00:00
Matthieu Riegler 9a764e0f5f refactor(core): remove ng when invoking enableProdMode
This was caught while investigating a leak.

(cherry picked from commit cb5879f566)
2026-01-13 19:39:53 +00:00
Andrew Scott 9c1a5a5433 build: update cherrypick changelog to stash
there are often some formatting/newline changes caused by other steps. just stash anything

(cherry picked from commit 029685bd93)
2026-01-13 19:20:20 +00:00
Kristiyan Kostadinov a649fc8f57 fix(vscode-extension): add syntax highlighting for arrow functions
Updates the syntax definition to handle arrow functions. The definition is largely based on TypeScript's own syntax highlighting since it's quite complex.

(cherry picked from commit b95753eb7d)
2026-01-13 19:09:30 +00:00
Kristiyan Kostadinov 8f16846dd9 fix(vscode-extension): add syntax highlighting for spread/rest expressions
Updates the syntax definition to include spread/rest expressions.

(cherry picked from commit c70a6a6ecc)
2026-01-13 19:09:29 +00:00
Jeremy Elbourn 114d03f6a6 docs: update OnChanges API reference w/ signal input
We had some reports of LLMs claiming that `ngOnChanges` does not include
changes for signal-based inputs. This is wrong (and the LLMs just made
it up), but we can update the docs at least to demonstrate `ngOnChanges`
with signal-based inputs.

Previously, the API reference used a real test as a code example, but
updating the test to a signal-based input is more involved than this
change needs to call for.

(cherry picked from commit 871fa1b2dd)
2026-01-13 17:04:28 +00:00
Angular Robot bf5db2b8d3 build: lock file maintenance
See associated pull request for more information.
2026-01-13 08:59:44 -08:00
Angular Robot c8b1b15143 docs: update cross-repo adev docs
Updated Angular adev cross repo docs files.
2026-01-13 08:44:21 -08:00
lyutails 559797c19d docs: update hierarchical-dependency-injection.md
VillainsService highly likely misspelled and then written as VillainService with 's' missing

(cherry picked from commit 910768f5cc)
2026-01-13 16:31:45 +00:00
Alan Agius 33ea12a797 build: remove @angular-devkit/architect-cli from dev dependencies
This dependency is not used.
2026-01-13 08:30:17 -08:00
Alan Agius 1dd06f5e5c ci: update Renovate configuration to replace Bazel module dependency command
This uses `ng-dev misc sync-module-bazel`, add `MODULE.bazel` to file filters, and remove specific dependency name matching.

(cherry picked from commit 6e82167481)
2026-01-13 16:28:47 +00:00
Jessica Janiuk 76a6984d7b release: bump VSCode extension version to 21.1.0 2026-01-12 16:35:27 -08:00
Andrew Scott f5ed514b41 build: improve changelog generation and tag fetching in release script
- Fetch tags explicitly to ensure local availability.
- Implement `getPreviousTag` to reliably determine the base for changelog generation, falling back to the latest `vsix-*` tag if the specific previous version tag is missing.
- Filter changelog commits by subject to exclude duplicates (e.g. cherry-picks) that are already present in the previous release history but have different hashes.

(cherry picked from commit 69da6390b1)
2026-01-13 00:06:39 +00:00
Andrew Scott 3396943fd0 build: fix changelog generation for last rev
updates changelog generation to work when releasing the first final
version.

(cherry picked from commit 78755771e1)
2026-01-12 22:53:23 +00:00
Andrew Scott eecbdc078e build: Set version of vscode to rc
this allows correct computation of the last version in teh release
script. we should update the rc branching to ensure this happens
automatically maybe
2026-01-12 14:49:27 -08:00
SkyZeroZx 40864e85da docs(docs-infra): replace deprecated Router.isActive with isActive (#66430)
Updates the view transition logic to use the isActive function instead of the deprecated Router.isActive

PR Close #66430
2026-01-12 22:42:33 +00:00
SkyZeroZx b1c68e66a7 docs: update examples to use isActive instead of deprecated Router.isActive (#66430)
PR Close #66430
2026-01-12 22:42:33 +00:00
Andrew Scott 757e0cfd26 build: use tsx for release script
I otherwise get Unknown file extension ".mts". using tsx fixes it for me

(cherry picked from commit e34365cfd5)
2026-01-12 22:41:17 +00:00
Matthieu Riegler 532633301b fix(forms): Ensure the control instruction comes after the other bindings
Prior to this change, binding to radio value was sensitive to the order in which `value` & `formField` where binding in the template.
The compiler change makes that order non-important.

fixes #66402

(cherry picked from commit 65fa5b5439)
2026-01-12 21:49:23 +00:00
cexbrayat 8474277b49 test(forms): improve test coverage for multiple pattern validators
Add comprehensive test cases to validate behavior when multiple
pattern validators are applied to the same field.

These tests clarify that multiple patterns operate with AND logic,
where each pattern is validated independently and produces its own
error when it doesn't match.

(cherry picked from commit 7fd076bdd3)
2026-01-12 21:48:20 +00:00
Shuaib Hasan Akib 5ad2e1bae0 docs: replace old https://angular.io/license with https://angular.dev/license
(cherry picked from commit 1b4dcc01ff)
2026-01-12 21:41:34 +00:00
Mikhail Efanov ab86c4dfaf build: update dev container configuration
build: update dev container configuration

build: update dev container configuration

Update dev container configuration files to support latest environment.
- Use customizations.vscode structure instead of deprecated extensions property.
- Update bazel extension to BazelBuild.vscode-bazel.
- Ignore .pnpm-store directory.
- Update ports list
- Use forwardPorts instead appPort

Document the usage of Dev Containers in building-and-testing-angular.md.

(cherry picked from commit 27276374ab)
2026-01-12 21:39:34 +00:00
Shuaib Hasan Akib 0587bb77ad fix(docs-infra): use margin instead of padding to prevent heading hover overflow
Replaced padding-block-start with margin-block-start on
.docs-reference-section-heading to prevent anchor hover and click areas from extending into empty space above the heading. This ensures hover behavior aligns with the visible text.

(cherry picked from commit f53f878e0f)
2026-01-12 21:38:50 +00:00
Matthieu Riegler 79f979a673 docs(docs-infra): Throw build error on invalid guide links
The list of valid links is generated from navigation data configuration in the ADEV app.
Redirections are knowingly exclude so we stop referencing them.

(cherry picked from commit c09f5352f6)
2026-01-12 21:38:23 +00:00
Shuaib Hasan Akib cc066cb31f fix(docs-infra): prevent duplicate description rendering for block API entries
Block entries (@if, @defer, @for,@let, @switch) were falling back to the generic
DocsReference template, causing the description to appear twice - once in
the header section and once in the main content area.

This commit adds a dedicated rendering path for block entries:
- Creates BlockEntryRenderable type and associated transforms
- Adds BlockReference template that uses RawHtml directly
- Modifies HeaderApi to accept hideDescription prop
- Updates processing and rendering pipelines to handle blocks

The fix ensures block documentation displays only one description section
while preserving the existing behavior for all other API entry types.

Update adev/shared-docs/pipeline/api-gen/rendering/transforms/block-transforms.mts

Co-authored-by: Matthieu Riegler <kyro38@gmail.com>
(cherry picked from commit 8bdd98ef41)
2026-01-12 21:37:58 +00:00
Roshith 3932bab633 docs: fix spelling mistake in documentation
correct typo where "youself" was written instead of "yourself"

(cherry picked from commit 933ca9e5dc)
2026-01-12 21:37:00 +00:00
Joey Perrott e66a8ef0da build: begin requring usage of release mode for releases
Start requiring the usage of the release merge-mode for allowing releases to be made from the repository

(cherry picked from commit 4d6a6aafee)
2026-01-12 21:36:22 +00:00
Angular Robot ed5b62a140 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-01-12 13:34:56 -08:00
Angelo Parziale 1f47e885fd build(docs-infra): Update dependency @xterm/xterm to v6
Update dependency @xterm/xterm to v6 and fix style

(cherry picked from commit 4d983e9904)
2026-01-12 18:16:28 +00:00
SkyZeroZx d1f6bcdacd docs: remove Component suffix in animations, HTTP, and SSR guides
Removes the unnecessary `Component` suffix from examples.
Also fixes button styling in the animations examples, which previously did not render correctly in dark mode.

(cherry picked from commit 511d9ca9d2)
2026-01-12 18:07:09 +00:00
Shuaib Hasan Akib ed292e398a docs: correct line number highlighting in query examples
Updates incorrect highlighted line numbers so they match the intended
code being referenced in the documentation.

(cherry picked from commit c63bc58e0f)
2026-01-12 18:04:18 +00:00
SkyZeroZx 3954dc278b refactor(http): remove redundant providedIn: 'root' in XSRF_HEADER_NAME
Removes unnecessary `providedIn: 'root'` declarations from injection token

(cherry picked from commit e38c1bf743)
2026-01-12 18:00:30 +00:00
SkyZeroZx 8cf3a72d57 refactor(core): Add missing OnDestroy to zone and zoneless change detection schedulers
Implements the OnDestroy interface in NgZoneChangeDetectionScheduler and ChangeDetectionSchedulerImpl

(cherry picked from commit fa6e82b569)
2026-01-12 18:00:29 +00:00
Matthieu Riegler 481557e69f docs(docs-infra): prevent from showing duplicate usage blocks in functions API docs
fixes #66398

(cherry picked from commit b22835579e)
2026-01-12 17:50:16 +00:00
Matthieu Riegler 13a7ee8424 docs(docs-infra): Show API decorators docs
fixes #66129

(cherry picked from commit a056413520)
2026-01-12 17:41:28 +00:00
Matthieu Riegler fd3253e093 docs(docs-infra): Add community russian translation
fixes #65720

(cherry picked from commit 33b2d9905d)
2026-01-12 17:40:11 +00:00
Matthieu Riegler addccead9b docs: change language section to "community translations"
(cherry picked from commit ad699cebb5)
2026-01-12 17:30:22 +00:00
Angular Robot 8f451b5647 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-01-12 08:55:39 -08:00
omar-almasry11 e4dcd72926 fix(docs-infra): improve symbolic green contrast for WCAG compliance
Adjusts the symbolic green color to meet WCAG contrast requirements in documentation styles.

(cherry picked from commit 405b05607c)
2026-01-12 16:50:37 +00:00
Jeevan Mahesha 204aacadb4 docs: update testing guide to include Vitest and improve authGuard test setup
(cherry picked from commit 66b40d4fb3)
2026-01-12 16:32:14 +00:00
Angular Robot e9cb3cbefb build: update pnpm to v10.28.0
See associated pull request for more information.
2026-01-12 08:31:22 -08:00
Matthieu Riegler 4871131136 docs: improve separation between enter and leave example
fixes #66359

(cherry picked from commit 7f1b790f84)
2026-01-12 16:18:20 +00:00
Kristiyan Kostadinov fb21801827 build: update license for vscode extension
The vscode extension still has the old license headers pointing pointing to AIO.

(cherry picked from commit f0dba6deb9)
2026-01-12 16:17:43 +00:00
aparziale 3de709f41b docs(docs-infra): Angular Aria listbox shows placeholder for "Selection modes"
Improved documentation for the aria listbox component. Added an example comparing selectionMode input.

Fixes: #65682
(cherry picked from commit e7f24014cf)
2026-01-12 16:17:17 +00:00
Angular Robot 47d89c8ca2 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-01-12 08:15:35 -08:00
SkyZeroZx c7d6053797 refactor(compiler-cli): update error message links to versioned docs (#66374)
Error message links now point to the archived documentation site (v*.angular.dev)
so that referenced content matches the framework version in use.

See angular#44650

PR Close #66374
2026-01-09 22:33:53 +00:00
SkyZeroZx 03e2b36826 refactor(core): update error message links to versioned docs (#66374)
Error message links now point to the archived documentation site (v*.angular.dev)
so that referenced content matches the framework version in use.

See angular#44650

PR Close #66374
2026-01-09 22:33:52 +00:00
Miles Malerba 29f074a93f fix(forms): Rename signal form [field] to [formField]
This completes the rename started in #66136. `[field]` is too generic of
a selector for the forms system to own, and likely to cause naming
collisions with existing components. Therefore it is being renamed to
`[formField]`

(cherry picked from commit 5671f2cc07)
2026-01-09 22:33:13 +00:00
tomer953 62d7533ca6 fix(migrations): prevent duplicate imports in common-to-standalone migration
The common-to-standalone migration did not check for existing imports
when adding needed imports after removing CommonModule. This change adds deduplication
logic to filter out imports that already exist in the imports array
before adding them, ensuring each import appears only once.

(cherry picked from commit 008e20ca56)
2026-01-09 22:30:52 +00:00
Angular Robot a59f51f0e6 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-01-09 11:26:35 -08:00
Alan Agius a201d33e0c ci: add pnpm and typescript exclusions to a Renovate package rule.
This will be handled in dev-infra preset, see: https://github.com/angular/dev-infra/pull/3340

(cherry picked from commit 99db2e9065)
2026-01-09 18:41:58 +00:00
fisker 0875deab8d refactor(compiler): switch Binary.isAssignmentOperation to type guard function
Improve `Binary.isAssignmentOperation` types

(cherry picked from commit 4fb24cadee)
2026-01-09 17:59:33 +00:00
Angular Robot dd37fd821e build: update cross-repo angular dependencies
See associated pull request for more information.
2026-01-09 09:56:00 -08:00
SkyZeroZx 7b396e8bb2 docs(docs-infra): avoid persistent AbortSignal listener in wait debounce in search
Cleans up the abort listener once the timeout resolves to prevent it from
remaining attached longer than necessary.

(cherry picked from commit 94b707957a)
2026-01-09 17:38:11 +00:00
Yahya-Almubarak 24b0920755 docs: replace UnambiguousRoleValidator in asyc-valdotors part form-validation.md
The async validators part of document uses uniqueRoleValidator and not UnambiguousRoleValidator.

(cherry picked from commit e3b3290998)
2026-01-09 17:26:22 +00:00
Devin Chasanoff f3a7b3ab67 docs: add ai tutor signal forms docs
(cherry picked from commit 09234753e1)
2026-01-09 17:25:43 +00:00
Angular Robot 1c055aa268 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-01-09 09:05:45 -08:00
kirjs 0c23d02944 docs(forms): Clarify returning errors from submit functions
Update outdate comment, and add a section to the docs

(cherry picked from commit 7d985ce08e)
2026-01-09 16:47:46 +00:00
fisker 83bac5adc9 refactor(compiler): tighten Unary.operator type
Improve `Unary.operator` type

(cherry picked from commit a7e470a1b3)
2026-01-09 16:47:05 +00:00
fisker e01dcae4c5 refactor: _ParseAST.isAssignmentOperator to type guard
Update `_ParseAST.isAssignmentOperator` to type guard

(cherry picked from commit 4d19408e9b)
2026-01-09 16:45:40 +00:00
fisker 4cdf4d5760 refactor(compiler): tighten Binary.operation type
Improve `Binary.operation` types

(cherry picked from commit 07a08ab9f8)
2026-01-09 16:45:39 +00:00
hawkgs f41d721ce0 refactor(devtools): style the profiler dialogs to match the current design
Decrease the font size and tone down the paddings and margins. Use `ng-button` instead of `mat-button`.

(cherry picked from commit e66aeac8d0)
2026-01-09 16:23:21 +00:00
hawkgs 0fd8477f3f refactor(devtools): add secondary type button
Add a secondary style/type button to `ng-button`.

(cherry picked from commit bea5840864)
2026-01-09 16:23:21 +00:00
SkyZeroZx 5dd2f2bb98 docs(docs-infra): improve label fallback logic in search results
Improves label fallback in search results by providing a fallback to the top-level category when a more specific label is missing.

(cherry picked from commit 3a65814f53)
2026-01-09 15:58:36 +00:00
SkyZeroZx ca57165c7e docs: add docs support for spread and rest operators in expression syntax
(cherry picked from commit 0d35088b56)
2026-01-09 15:58:11 +00:00
Angular Robot 4391c44801 build: update cross-repo angular dependencies to v21.1.0-next.4
See associated pull request for more information.
2026-01-09 07:50:14 -08:00
Alon Mishne 985bdeefd3 docs: Updated MCP docs to include latest experimental tools and recent renames
(cherry picked from commit c963f35d2c)
2026-01-09 15:48:26 +00:00
Matthieu Riegler 95bc06d6c3 Revert "docs: remove incorrectly listed operators in "Supported operators" table (Expression Syntax Guide) (#62092)"
This reverts commit ea71a1e8cd.

(cherry picked from commit 80a7419a16)
2026-01-09 15:47:52 +00:00
Angular Robot 2c3a067bb6 build: update cross-repo angular dependencies
See associated pull request for more information.
2026-01-08 14:42:26 -08:00
Angular Robot 2c74475969 docs: update cross-repo adev docs
Updated Angular adev cross repo docs files.
2026-01-08 14:41:15 -08:00
Andrew Scott 74af7d85e5 refactor(core): Use the provided Document value rather than global in FakeNavigation
This commit ensures the Document used by `FakeNavigation` is the one
passed in the constructor rather than the global `document`, which may
be different.

(cherry picked from commit 49295778bc)
2026-01-08 21:28:23 +00:00
Jessica Janiuk 6ad2ef6bd8 release: cut the v21.1.0-rc.0 release 2026-01-08 12:50:38 -08:00
1187 changed files with 69818 additions and 96087 deletions
-1
View File
@@ -1 +0,0 @@
../../AGENTS.md
-1
View File
@@ -1 +0,0 @@
../.gemini/skills
+1 -1
View File
@@ -1 +1 @@
8.6.0
8.5.1
-70
View File
@@ -1,70 +0,0 @@
description = "Researches part of the codebase and creates a skill for it"
prompt="""
## Role
You are an experienced senior developer specializing in reverse engineering and understanding
complex code, prompt engineering, and context engineering.
Your task is to build a complete understanding of the mental model and architecture of the code,
and to create an efficient context file that allows a coding agent to understand and work with the
code.
## Guiding Principles
1. **Deep Analysis:** Understand the _why_ behind the code. Not just a surface level listing of
files and classes.
2. **Curiosity:** Be curious. If something is unclear, confusing, or unexpected you _MUST_
investigate further until you fully understand.
3. **Efficient Token Use:** The final output will be directly included into the context of the
coding assistant, so be sure to make efficient use of tokens. Think deeply, but only capture the
most important information for the coding agent in the final output
## Procedure
1. Fully explore the code before first, with a focus on understanding the architecture and how all
of the pieces fit together.
2. Develop an understanding of the mental model behind the architecture. _Why_ is the code is
architected this way? How should developers think about the system?
3. Document your findings using the given output format described in the next section.
## Output Format
Note the following placeholders below:
- `[package]`: is a unique identifier for the current code package (for example if you're looking at
code under `packages/forms/signals` you might use `signal-forms` as the `[package]`)
- `[code-location]`: is the location of the current code within the larger repository
- `[context]`: is the context to add to the coding agent when it is working with this code.
With those placeholders in mind, write a file create a new directory under `.gemini/skills` with the
name `reference-[package]/`, and under that directory add a `SKILL.md` file with the following
content:
```md
---
name: reference-[package]
description: Explains the mental model and architecture of the code under `[code-location]`. You MUST use this skill any time you plan to work with code in `[code-location]`
---
[context]
```
## Additional User Instructions
The user may have additional instructions.
- The user should indicate what package to research
- The user may indicate that they want to update a package that has already been researched. In this
case, read the existing skill and make any required updates based on your research
- The user may request supplemental information be added to the skill (if so, add this to the
existing skill in a new markdown file under a `references` directory within the skill. Add a
section to the main `SKILL.md` that mentions what supplemental information is available)
Here are the user's additional instructions:
{{args}}
"""
-5
View File
@@ -1,5 +0,0 @@
{
"context": {
"fileName": ["AGENTS.md", "GEMINI.md"]
}
}
-178
View File
@@ -1,178 +0,0 @@
---
name: adev-writing-guide
description: Comprehensive writing guide for Angular documentation (adev). Covers Google Technical Writing standards, Angular-specific markdown extensions, code blocks, and components. Use when authoring or reviewing content in adev/src/content.
---
# Angular Documentation (adev) Writing Guide
This skill provides comprehensive guidelines for authoring content in `adev/src/content`. It combines Google's technical writing standards with Angular-specific markdown conventions, components, and best practices.
## I. Google Technical Writing Guidelines
### Tone and Content
- **Be conversational and friendly:** Maintain a helpful yet professional tone. Avoid being overly casual.
- **Write accessibly:** Ensure documentation is understandable to a diverse global audience, including non-native English speakers.
- **Audience-first:** Focus on what the user needs to do, not just what the system does.
- **Avoid pre-announcing:** Do not mention unreleased features or make unsupported claims.
- **Use descriptive link text:** Link text should clearly indicate the destination (e.g., avoid "click here").
### Language and Grammar
- **Use second person ("you"):** Address the reader directly.
- **Prefer active voice:** Clearly state who or what is performing the action (e.g., "The system generates a token" vs "A token is generated").
- **Standard American English:** Use standard American spelling and punctuation.
- **Conditional clauses first:** Place "if" or "when" clauses before the instruction (e.g., "If you encounter an error, check the logs").
- **Define terms:** Introduce new or unfamiliar terms/acronyms upon first use.
- **Consistent terminology:** Use the same term for the same concept throughout the document.
- **Conciseness:** Aim for one idea per sentence. Keep sentences short.
### Formatting and Organization
- **Sentence case for headings:** Capitalize only the first word and proper nouns in titles and headings.
- **Lists:**
- **Numbered lists:** Use for sequential steps or prioritized items.
- **Bulleted lists:** Use for unordered collections of items.
- **Description lists:** Use for term-definition pairs.
- **Serial commas:** Use the Oxford comma (comma before the last item in a list of three or more).
- **Code formatting:** Use code font for code-related text (filenames, variables, commands).
- **UI Elements:** formatting user interface elements in **bold**.
- **Date formatting:** Use unambiguous formats (e.g., "September 4, 2024" rather than "9/4/2024").
- **Structure:** Use logical hierarchy with clear introductions and navigation. Headings should be task-based where possible.
### Images and Code Samples
- **Images:** Use simple, clear illustrations to enhance understanding.
- **Captions:** Write captions that support the image.
- **Code Samples:**
- Ensure code is correct and builds without errors.
- Follow language-specific conventions.
- **Comments:** Focus on _why_, not _what_. Avoid commenting on obvious code.
### Reference Hierarchy
1. Project-specific style guidelines (if any exist in `CONTRIBUTING.md` or similar).
2. Google Developer Documentation Style Guide.
3. Merriam-Webster (spelling).
4. Chicago Manual of Style (non-technical).
5. Microsoft Writing Style Guide (technical).
---
## II. Angular Documentation Specifics
### Code Blocks
Use the appropriate language identifier for syntax highlighting:
- **TypeScript (Angular):** Use `angular-ts` when TypeScript code examples contain inline templates.
- **HTML (Angular):** Use `angular-html` for Angular templates.
- **TypeScript (Generic):** Use `ts` for plain TypeScript.
- **HTML (Generic):** Use `html` for plain HTML.
- **Shell/Terminal:** Use `shell` or `bash`.
- **Mermaid Diagrams:** Use `mermaid`.
#### Attributes
You can enhance code blocks with attributes in curly braces `{}` after the language identifier:
- `header="Title"`: Adds a title to the code block.
- `linenums`: Enables line numbering.
- `highlight="[1, 3-5]"`: Highlights specific lines.
- `hideCopy`: Hides the copy button.
- `prefer`: Marks code as a preferred example (green border/check).
- `avoid`: Marks code as an example to avoid (red border/cross).
**Example:**
````markdown
```angular-ts {header:"My Component", linenums, highlight="[2]"}
@Component({
selector: 'my-app',
template: '<h1>Hello</h1>',
})
export class App {}
```
````
#### `<docs-code>` Component
For more advanced code block features, use the `<docs-code>` component:
- `path`: Path to a source file (e.g., `adev/src/content/examples/...`).
- `header`: Custom header text.
- `language`: Language identifier (e.g., `angular-ts`).
- `linenums`: Boolean attribute.
- `highlight`: Array of line numbers/ranges (e.g., `[[3,7], 9]`).
- `diff`: Path to diff file.
- `visibleLines`: Range of lines to show initially (collapsible).
- `region`: Region to extract from source file.
- `preview`: Boolean. Renders a live preview (StackBlitz). _Only works with standalone examples._
- `hideCode`: Boolean. Collapses code by default.
**Multifile Example:**
```html
<docs-code-multifile path="..." preview>
<docs-code path="..." />
<docs-code path="..." />
</docs-code-multifile>
```
### Alerts / Admonitions
Use specific keywords followed by a colon for alerts. These render as styled blocks.
- `NOTE:` For ancillary information.
- `TIP:` For helpful hints or shortcuts.
- `IMPORTANT:` For crucial information.
- `CRITICAL:` For warnings about potential data loss or severe issues.
- `TODO`: For incomplete documentation.
- `QUESTION:` To pose a question to the reader.
- `SUMMARY:` For section summaries.
- `TLDR:` For concise summaries.
- `HELPFUL:` For best practices.
**Example:**
```markdown
TIP: Use `ng serve` to run your application locally.
```
### Custom Components
- **Cards (`<docs-card>`):**
- Must be inside `<docs-card-container>`.
- Attributes: `title`, `link`, `href`.
- **Callouts (`<docs-callout>`):**
- Attributes: `title`, `important`, `critical`.
- **Pills (`<docs-pill>`):**
- Must be inside `<docs-pill-row>`.
- Attributes: `title`, `href`.
- **Steps / Workflow (`<docs-step>`):**
- Must be inside `<docs-workflow>`.
- Attributes: `title`.
- **Tabs (`<docs-tab>`):**
- Must be inside `<docs-tab-group>`.
- Attributes: `label`.
- **Videos (`<docs-video>`):**
- Attributes: `src` (YouTube embed URL), `alt`.
### Images
Use standard markdown syntax with optional attributes for sizing and loading behavior.
- `#small`, `#medium`: Append to image URL for sizing.
- `{loading: 'lazy'}`: Add attribute for lazy loading.
**Example:**
```markdown
![Alt Text](path/to/image.png#medium {loading: 'lazy'})
```
### Headers
- Use markdown headers (`#`, `##`, `###`).
- Ensure a logical hierarchy (don't skip levels).
- `h2` and `h3` are most common for content structure.
@@ -1,91 +0,0 @@
---
name: reference-compiler-cli
description: Explains the mental model and architecture of the code under `packages/compiler-cli`. You MUST use this skill any time you plan to work with code in `packages/compiler-cli`
---
# Angular Compiler CLI (`ngtsc`) Architecture
## Overview
The `packages/compiler-cli` package contains the Angular Compiler (Ivy), often referred to as `ngtsc`. It is a wrapper around the TypeScript compiler (`tsc`) that extends it with Angular-specific capabilities.
The core goal of `ngtsc` is to compile Angular decorators (like `@Component`, `@Directive`, `@Pipe`) into static properties on the class (Ivy instructions, e.g., `static ɵcmp = ...`). It also performs template type checking and ahead-of-time (AOT) compilation.
## Mental Model
The compiler is designed as a **lazy, incremental, and partial** compilation pipeline.
1. **Wrapper Pattern**: `NgtscProgram` wraps the standard `ts.Program`. It intercepts calls to act as a drop-in replacement for standard tooling.
2. **Traits System**: Every class with an Angular decorator is considered a "Trait". The compiler manages the state of these traits through a state machine:
- **Pending**: Detected but not processed.
- **Analyzed**: Metadata extracted, template parsed (but dependencies not yet linked).
- **Resolved**: Dependencies (directives/pipes in template) resolved, import cycles handled.
- **Skipped**: Not an Angular class.
3. **Lazy Analysis**: Analysis only happens when necessary (e.g., when diagnostics are requested or emit is prepared).
4. **Output AST**: The compiler generates an intermediate "Output AST" (`o.Expression`) for the generated code, which is then translated into TypeScript AST nodes during the emit phase.
## Key Subsystems
### 1. Core Orchestration (`ngtsc/core`)
- **`NgtscProgram`**: The public API implementing `api.Program`. It manages the `ts.Program` and the `NgCompiler`.
- **`NgCompiler`**: The brain of the compiler. It orchestrates the compilation phases (Analysis, Resolution, Type Checking, Emit). It holds the `TraitCompiler`.
### 2. Trait Compilation (`ngtsc/transform`)
- **`TraitCompiler`**: Manages the lifecycle of "Traits". It iterates over source files, identifies decorated classes, and delegates to the appropriate `DecoratorHandler`.
- **`Trait`**: A state container for a class, holding its handler, analysis results, and resolution results.
### 3. Decorator Handlers (`ngtsc/annotations`)
- **`DecoratorHandler`**: An interface for handling specific decorators.
- **`ComponentDecoratorHandler`**: The most complex handler. It:
- Extracts metadata (selector, inputs, outputs).
- Parses the template.
- Resolves used directives and pipes (`R3TargetBinder`).
- Generates the `ɵcmp` instruction.
- **`DirectiveDecoratorHandler`**, **`PipeDecoratorHandler`**, **`NgModuleDecoratorHandler`**: Handle their respective decorators.
### 4. Template Type Checking (`ngtsc/typecheck`)
- **`TemplateTypeChecker`**: Generates "Type Check Blocks" (TCBs). A TCB is a block of TypeScript code that represents the template's logic in a way `tsc` can understand and check for errors.
- **`TypeCheckBlock`**: The actual generated code that validates bindings, events, and structural directives.
### 5. Metadata & Scope (`ngtsc/metadata`, `ngtsc/scope`)
- **`MetadataReader`**: Reads Angular metadata from source files (using `LocalMetadataRegistry`) and `.d.ts` files (using `DtsMetadataReader`).
- **`ScopeRegistry`**: Determines the "compilation scope" of a component (which directives/pipes are available to it), handling `NgModule` transitive exports and Standalone Component imports.
### 6. Emit & Transformation (`ngtsc/transform`)
- **`ivyTransformFactory`**: A TypeScript transformer factory.
- **`IvyCompilationVisitor`**: Visits classes, triggers compilation via `TraitCompiler`, and collects the Output AST.
- **`IvyTransformationVisitor`**: Translates the Output AST into TypeScript AST, injects the `static ɵ...` fields, and removes the original decorators.
## Compilation Phases
1. **Construction**: `NgtscProgram` creates `NgCompiler`, which sets up all registries and the `TraitCompiler`.
2. **Analysis** (`analyzeSync`):
- The `TraitCompiler` scans files.
- `DecoratorHandler`s extract metadata and parse templates.
- No cross-file resolution happens here (allowing for parallelism and caching).
3. **Resolution** (`resolve`):
- `TraitCompiler` resolves traits.
- Components link their templates to specific Directives and Pipes (found via `ScopeRegistry`).
- Import cycles are detected and handled (e.g., via "remote scoping").
4. **Type Checking**:
- `TemplateTypeChecker` creates TCBs for all components.
- TypeScript diagnostics are retrieved for these TCBs.
5. **Emit** (`prepareEmit`):
- `ivyTransformFactory` is created.
- TS `emit` is called.
- The transformers run, injecting the compiled Ivy instructions into the JS/DTS output.
## Important File Locations
- `packages/compiler-cli/src/ngtsc/program.ts`: Entry point (`NgtscProgram`).
- `packages/compiler-cli/src/ngtsc/core/src/compiler.ts`: Core logic (`NgCompiler`).
- `packages/compiler-cli/src/ngtsc/transform/src/trait.ts`: Trait state machine.
- `packages/compiler-cli/src/ngtsc/annotations/component/src/handler.ts`: Component compilation logic.
- `packages/compiler-cli/src/ngtsc/typecheck/src/template_type_checker.ts`: Type checking logic.
- `packages/compiler-cli/src/ngtsc/transform/src/transform.ts`: AST transformation logic.
-96
View File
@@ -1,96 +0,0 @@
---
name: reference-core
description: Explains the mental model and architecture of the code under `packages/core`. You MUST use this skill any time you plan to work with code in `packages/core`
---
# Angular Core (`packages/core`) Mental Model
This document outlines the architecture and mental model for `packages/core`, the heart of the Angular framework.
## 1. High-Level Architecture
`packages/core` contains the runtime logic for Angular. Its primary responsibilities are:
1. **Rendering (Ivy/Render3)**: Transforming templates into DOM updates.
2. **Dependency Injection (DI)**: Managing object creation and lifetime.
3. **Change Detection**: Synchronizing the model with the view.
4. **Reactivity**: Signals and Zone.js integration.
## 2. Rendering Engine (Ivy / Render3)
The rendering engine (located in `packages/core/src/render3`) uses an **instruction-based** approach.
### Key Concepts
- **Instructions**: The Angular compiler transforms templates into a sequence of instruction calls (e.g., `ɵɵelementStart`, `ɵɵtext`, `ɵɵproperty`). These instructions are executed at runtime to create and update the view.
- _Location_: `packages/core/src/render3/instructions`
- **LView (Logical View)**: An array containing the _state_ of a specific view instance. It holds:
- DOM nodes (`RElement`, `RText`).
- Binding values (for change detection).
- Directive/Component instances.
- _Context_: `packages/core/src/render3/interfaces/view.ts`
- **TView (Template View)**: An array containing the _static structure_ of a view. It is shared across all instances (`LView`s) of the same component/template. It holds:
- Property names for bindings.
- Node relationship information.
- Compiled directive definitions.
- _Context_: `packages/core/src/render3/interfaces/view.ts`
- **Memory Layout**: `LView` and `TView` are parallel arrays. Index `i` in `LView` corresponds to metadata at index `i` in `TView`.
- `HEADER`: Fixed size, contains context (Parent, Host, etc.).
- `DECLS`: Static nodes (elements, text, pipes).
- `VARS`: Binding values.
- `EXPANDO`: Dynamic data (host bindings, injectors).
### The Render Cycle
1. **Creation Mode**: Instructions create DOM nodes and store them in `LView`.
2. **Update Mode**: Instructions check current values against previous values stored in `LView`. If changed, they update the DOM.
## 3. Dependency Injection (DI)
DI in Angular is hierarchical and split into two systems that interact:
### Module Injector (`R3Injector`)
- Configured via `@NgModule.providers` or `providedIn: 'root'`.
- Stored in a hierarchy of `R3Injector` instances.
- _Location_: `packages/core/src/di/r3_injector.ts`
### Node Injector
- Configured via `@Component.providers` or `@Directive.providers`.
- **Not a class**, but a data structure embedded in the `LView` ("Expando" section).
- Uses **Bloom Filters** (`TView.data`) to quickly check if a token is present at a specific node index before traversing up the tree.
- Resolves tokens starting from the current node, walking up the view tree (Element Injector hierarchy), and falling back to the Module Injector if not found.
## 4. Change Detection
- **Dirty Checking**: Angular checks if values bound in templates have changed.
- **Strategies**:
- `Default`: Checks everything.
- `OnPush`: Checks only if inputs change, events fire, or signals update.
- **Signals**: The new reactivity primitive. Signals notify the scheduler when they change, potentially allowing for fine-grained updates (Zoneless).
## 5. Key Directories to Know
- `src/render3`: The Ivy rendering engine.
- `instructions`: The runtime instructions called by compiled code.
- `interfaces`: `LView`, `TView`, `TNode` definitions.
- `src/di`: Dependency injection system.
- `src/change_detection`: Change detection logic.
- `src/zone`: Zone.js integration.
- `src/signal`: Signals implementation (if present in this version, otherwise likely in `primitives`).
## 6. Conventions & Gotchas
- **Prefixes**: Private/Internal exports often start with `ɵ`.
- **Global State**: Ivy relies heavily on global state (e.g., `getLView()`) during instruction execution to avoid passing context arguments everywhere. This is for performance and code size.
- **Performance**: The code is highly optimized for performance and memory. You will see arrays used instead of objects, bitmasks, and manual memory management patterns. **Respect these patterns.**
## 7. How to Modify Core
1. **Understand the Instruction**: If modifying runtime behavior, find the corresponding instruction in `src/render3/instructions`.
2. **Check `LView`/`TView` Impact**: If adding state, understand where it fits in the `LView` array.
3. **Tests**: Core has extensive tests. Run them using Bazel.
@@ -1,104 +0,0 @@
---
name: reference-signal-forms
description: Explains the mental model and architecture of the code under `packages/forms/signals`. You MUST use this skill any time you plan to work with code in `packages/forms/signals`
---
# Signal Forms Architecture
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
1. **Model-Driven**: The system is built around a `WritableSignal<T>` which serves as the **single source of truth**.
Unlike Reactive Forms where the `FormControl` holds the value, here the `Signal` holds the value.
The form is merely a _view_ or _projection_ of that signal, adding form-specific state (validity, dirty, touched).
2. **Proxy-Based Traversal**: The form API (`form(signal)`) returns a `FieldTree`. This object is a **Proxy**.
It allows accessing nested fields (e.g., `myForm.user.name`) without manually creating control groups.
Accessing a property on the proxy lazily resolves or creates the corresponding `FieldNode`.
3. **Schema-Based Logic**: Validation, disabled state, and other metadata are defined separately via **Schemas**.
Schemas are applied to the form structure using functions like `apply`, `applyEach` (for arrays), and `applyWhen`.
This separates the _structure_ of the data from the _rules_ governing it.
4. **Directives as Glue**: The `[formField]` directive binds a DOM element (native input or custom control) to a `FieldNode`.
It handles:
- Syncing the value between the DOM and the Signal.
- Reflecting state (valid, touched, etc.) to the UI.
- Handling user interaction events (blur, input).
## Key Components
### 1. `FieldNode` (`src/field/node.ts`)
The central internal class representing a single field in the form graph. It aggregates several state managers:
- `structure`: Manages parent/child relationships and signal slicing.
- `validationState`: Computes `valid`, `invalid`, `errors` signals.
- `nodeState`: Tracks `touched`, `dirty`, `pristine`.
- `metadataState`: Stores metadata like `min`, `max`, `required`.
- `submitState`: Tracks submission status and server errors.
### 2. `ValidationState` (`src/field/validation.ts`)
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_directive.ts`)
The bridge between the `FieldNode` and the DOM.
- Selector: `[formField]`
- It supports:
- **Native Elements**: `<input>`, `<select>`, `<textarea>`.
- **Custom Controls**: Components implementing `FormUiControl` or `FormValueControl`.
- **Legacy Interop**: Components implementing `ControlValueAccessor` (via `InteropNgControl`).
### 4. `Schema` (`src/api/structure.ts` & `src/api/rules`)
Defines the behavior.
- Created via `schema(fn)`.
- Applied via `apply(path, schema)`.
- Rules include validators (`required`, `pattern`, `min`, `max`) and state modifiers (`disabled`, `hidden`).
## Data Flow
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.
## Usage Example (Conceptual)
```typescript
// 1. Define Model
const user = signal({name: '', age: 0});
// 2. Define Schema
const userRules = schema((u) => {
required(u.name);
min(u.age, 18);
});
// 3. Create Form
const userForm = form(user, userRules); // OR apply(userForm, userRules)
// 4. Bind in Template
// <input [formField]="userForm.name">
```
## Important Files
- `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_directive.ts`: The `[formField]` directive.
## Supplemental Information
- [Compiler & Core Integration](references/integration.md): Details how `[formField]` hooks into type-checking and the runtime.
@@ -1,46 +0,0 @@
# Signal Forms Integration: Compiler & Core
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-CLI Integration (Type Checking)
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:
- **Detection**: The compiler identifies a directive as a "Field Directive" if it has the `ɵNgFieldDirective` property or comes from `@angular/forms/signals`.
- **Synthetic Binding Expansion**: When you write `<input [formField]="mySignal" />`, the type checker doesn't just check `formField`. It synthetically expands this into a set of bindings for validation:
- `[value]="mySignal()"` (or `checked` for checkboxes)
- `[disabled]="mySignal.disabled()"`
- `[required]="mySignal.required()"`
- ...and so on.
- This ensures that `mySignal` (the `FieldNode`) has all the necessary properties to drive the form control.
- **Conflict Detection**: It actively prevents "double binding". If you bind `[formField]`, you are _banned_ from also binding `[value]`, `[disabled]`, `[required]`, etc., as the signal form should be the single source of truth.
- **Element Type Validation**:
- **Native Elements**: Checks that the signal's value type matches the element type (e.g., `<input type="checkbox">` requires a `boolean` signal).
- **Custom Controls**: Detects if a custom component is a "Form Value Control" (has a `value` input/output) or "Form Checkbox Control" (has a `checked` input/output) and validates against that.
### Relevant Files:
- `packages/compiler-cli/src/ngtsc/typecheck/src/ops/signal_forms.ts`: The core logic for `TcbNativeFieldOp` and `SignalFormFieldOp`.
## 2. Core Runtime Integration
The `packages/core` package provides the low-level instructions that power the `FormField` directive. This allows it to do things normal directives cannot, like efficiently syncing state without change detection overhead for every property.
### Key Mechanisms:
- **`ɵ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:
- **Access the Element**: Get direct access to the native element or component instance.
- **Set Inputs**: Write directly to inputs of _other directives_ on the same node (e.g., setting the `value` input of a custom control).
- **Listen to Outputs**: Subscribe to outputs of other directives (e.g., `valueChange`).
- **Bypass Templates**: It effectively acts as a "meta-directive" that manages the bindings for you, bypassing the need for explicit template syntax for every property.
### Relevant Files:
- `packages/core/src/render3/instructions/control.ts`: Implementation of `ɵɵcontrol` instructions.
- `packages/forms/signals/src/directive/form_field_directive.ts`: The directive that implements the hook.
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -5,9 +5,9 @@ runs:
using: 'composite'
steps:
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Saucelabs Variables
uses: angular/dev-infra/github-actions/saucelabs@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/saucelabs@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Starting Saucelabs tunnel service
shell: bash
run: ./tools/saucelabs/sauce-service.sh run &
+4 -4
View File
@@ -21,17 +21,17 @@ jobs:
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'adev: preview'))
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Build adev
# `snapshot-build` config is used to stamp the exact version with sha in the footer.
run: pnpm bazel build //adev:build.production --config=snapshot-build
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
with:
workflow-artifact-name: 'adev-preview'
pull-number: '${{github.event.pull_request.number}}'
+2 -2
View File
@@ -29,7 +29,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
with:
token: '${{secrets.GITHUB_TOKEN}}'
@@ -40,7 +40,7 @@ jobs:
npx -y firebase-tools@latest target:clear --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs
npx -y firebase-tools@latest target:apply --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs ${{env.PREVIEW_SITE}}
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
with:
github-token: '${{secrets.GITHUB_TOKEN}}'
workflow-artifact-name: 'adev-preview'
@@ -17,6 +17,6 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: angular/dev-infra/github-actions/branch-manager@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
- uses: angular/dev-infra/github-actions/branch-manager@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+1 -1
View File
@@ -38,7 +38,7 @@ jobs:
- run: pnpm install --frozen-lockfile
- uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
- uses: angular/dev-infra/github-actions/bazel/configure-remote@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
with:
bazelrc: ./.bazelrc.user
+26 -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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Check code lint
@@ -32,8 +32,6 @@ jobs:
run: pnpm ng-dev pullapprove verify
- name: Validate angular robot configuration
run: pnpm ng-dev ngbot verify
- name: Validate agent skills
run: pnpm ng-dev ai skills validate
- name: Confirm code builds with typescript as expected
run: pnpm check-tooling-setup
@@ -41,13 +39,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
with:
disable-package-manager-cache: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -57,7 +55,7 @@ jobs:
- name: Test build
run: pnpm devtools:build:chrome
- name: Cypress run
uses: cypress-io/github-action@bc22e01685c56e89e7813fd8e26f33dc47f87e15 # v7.1.5
uses: cypress-io/github-action@f790eee7a50d9505912f50c2095510be7de06aa7 # v6.10.9
with:
command: pnpm devtools:test:e2e
start: pnpm bazel run //devtools/src:devserver
@@ -69,11 +67,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -85,11 +83,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -102,11 +100,11 @@ jobs:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -121,11 +119,11 @@ jobs:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -138,11 +136,11 @@ jobs:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Install node modules
run: pnpm install --frozen-lockfile
- run: echo "https://${{secrets.SNAPSHOT_BUILDS_GITHUB_TOKEN}}:@github.com" > ${HOME}/.git_credentials
@@ -154,11 +152,11 @@ jobs:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -208,11 +206,11 @@ jobs:
runs-on: ubuntu-latest-8core
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Build adev
+3 -16
View File
@@ -3,36 +3,23 @@ name: DevInfra
on:
pull_request_target:
types: [opened, synchronize, reopened]
issues:
types: [opened, reopened]
# Declare default permissions as read only.
permissions:
contents: read
jobs:
pull_request_labels:
if: github.event_name == 'pull_request_target'
labels:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: angular/dev-infra/github-actions/labeling/pull-request@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
- uses: angular/dev-infra/github-actions/pull-request-labeling@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
labels: '{"requires: TGP": ["packages/core/primitives/**/{*,.*}"]}'
post_approval_changes:
if: github.event_name == 'pull_request_target'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: angular/dev-infra/github-actions/post-approval-changes@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
- uses: angular/dev-infra/github-actions/post-approval-changes@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
issue_labels:
if: github.event_name == 'issues'
runs-on: ubuntu-latest
steps:
- uses: angular/dev-infra/github-actions/labeling/issue@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
google-generative-ai-key: ${{ secrets.GOOGLE_GENERATIVE_AI_KEY }}
+1 -1
View File
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: angular/dev-infra/github-actions/google-internal-tests@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
- uses: angular/dev-infra/github-actions/google-internal-tests@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
with:
run-tests-guide-url: http://go/angular-g3sync-start
github-token: ${{ secrets.GITHUB_TOKEN }}
+4 -4
View File
@@ -13,15 +13,15 @@ jobs:
JOBS: 2
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Saucelabs Variables
uses: angular/dev-infra/github-actions/saucelabs@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/saucelabs@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Set up Sauce Tunnel Daemon
run: pnpm bazel run //tools/saucelabs-daemon/background-service -- $JOBS &
env:
+1 -1
View File
@@ -9,6 +9,6 @@ jobs:
status:
runs-on: ubuntu-latest
steps:
- uses: angular/dev-infra/github-actions/unified-status-check@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
- uses: angular/dev-infra/github-actions/unified-status-check@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+3 -3
View File
@@ -21,7 +21,7 @@ jobs:
workflows: ${{ steps.workflows.outputs.workflows }}
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Install node modules
run: pnpm install --frozen-lockfile
- id: workflows
@@ -36,9 +36,9 @@ jobs:
workflow: ${{ fromJSON(needs.list.outputs.workflows) }}
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Install node modules
run: pnpm install --frozen-lockfile
# We utilize the google-github-actions/auth action to allow us to get an active credential using workflow
+21 -23
View File
@@ -19,7 +19,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Check code lint
@@ -32,14 +32,12 @@ jobs:
run: pnpm ng-dev ngbot verify
- name: Confirm code builds with typescript as expected
run: pnpm check-tooling-setup
- name: Validate agent 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@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/linting/licenses@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
with:
allow-dependencies-licenses: 'pkg:npm/google-protobuf@'
@@ -47,13 +45,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
with:
disable-package-manager-cache: true
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Run unit tests
@@ -61,7 +59,7 @@ jobs:
- name: Test build
run: pnpm devtools:build:chrome
- name: Cypress run
uses: cypress-io/github-action@bc22e01685c56e89e7813fd8e26f33dc47f87e15 # v7.1.5
uses: cypress-io/github-action@f790eee7a50d9505912f50c2095510be7de06aa7 # v6.10.9
with:
command: pnpm devtools:test:e2e
start: pnpm bazel run //devtools/src:devserver
@@ -73,11 +71,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Run CI tests for framework
@@ -97,11 +95,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Run integration CI tests for framework
@@ -112,11 +110,11 @@ jobs:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Run tests
@@ -129,11 +127,11 @@ jobs:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Run tests
@@ -144,11 +142,11 @@ jobs:
labels: ubuntu-latest
steps:
- name: Initialize environment
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@3bec1182014f8cd3201bfc7d2bc6121658b9e0cd
- name: Install node modules
run: pnpm install --frozen-lockfile
- run: |
+1 -1
View File
@@ -47,6 +47,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: 'Upload to code-scanning'
uses: github/codeql-action/upload-sarif@0d579ffd059c29b07949a3cce3983f0780820c98 # v4.32.6
uses: github/codeql-action/upload-sarif@b20883b0cd1f46c72ae0ba6d1090936928f9fa30 # v4.32.0
with:
sarif_file: results.sarif
+1 -1
View File
@@ -9,5 +9,5 @@ export const github = {
name: 'angular',
mainBranchName: 'main',
mergeMode: 'caretaker-only',
requireReleaseModeForRelease: false,
requireReleaseModeForRelease: true,
};
-1
View File
@@ -16,7 +16,6 @@
"packages/docs/**",
"packages/elements/schematics/**",
"packages/examples/**",
"packages/forms/signals/src/api/rules/validation/standard_schema.ts",
"packages/localize/**",
"packages/private/**",
"packages/service-worker/**",
+1 -1
View File
@@ -1 +1 @@
22.22.1
22.22.0
+1 -7
View File
@@ -30,16 +30,10 @@ vscode-ng-language-service/syntaxes/test/data/*.html
# Ignore goldens MD files
goldens/**/*.api.md
# Ignore golden symbol json files
packages/**/*.golden_symbols.json
# adev generated files
adev/src/content/aria/**/*.json
adev/src/content/cli/**/*.json
adev/src/content/cdk/**/*.json
# Antigravity rules
.agent/rules/agents.md
CHANGELOG.md
CHANGELOG_ARCHIVE.md
CHANGELOG_ARCHIVE.md
+23 -28
View File
@@ -106,6 +106,7 @@ groups:
- thePunderWoman
- kirjs
- JoostK
- mmalerba
- ~amishne
- ~leonsenft
- ~mattrbeck
@@ -150,8 +151,10 @@ groups:
- crisbeto
- devversion
- kirjs
- ~jelbourn
- thePunderWoman
- ~pkozlowski-opensource
- mmalerba
- JeanMeche
- ~amishne
- ~leonsenft
@@ -178,9 +181,9 @@ groups:
reviewers:
users:
- ~alxhub
- jelbourn
- josephperrott
- ~pkozlowski-opensource
- alan-agius4
reviews:
request: -1 # request reviews from everyone
required: 2 # require at least 2 approvals
@@ -199,6 +202,7 @@ groups:
reviewers:
users:
- ~JiaLiPassion
- ~jelbourn
- ~alxhub
- AndrewKushnir
- atscott
@@ -207,6 +211,7 @@ groups:
- kirjs
- thePunderWoman
- ~pkozlowski-opensource
- mmalerba
- ~amishne
- ~leonsenft
- ~mattrbeck
@@ -243,8 +248,6 @@ groups:
- >
contains_any_globs(files, [
'adev/**/{*,.*}',
'tools/manual_api_docs/blocks/*.md',
'tools/manual_api_docs/elements/*.md',
])
reviewers:
users:
@@ -256,12 +259,14 @@ groups:
- crisbeto
- kirjs
- JeanMeche
- jelbourn
- thePunderWoman
- devversion
- josephperrott
- ~pkozlowski-opensource
- ~mgechev
- MarkTechson
- mmalerba
- ~hawkgs
- ~amishne
- ~leonsenft
@@ -297,13 +302,8 @@ groups:
<<: *defaults
conditions:
- >
contains_any_globs(files
.exclude('.pullapprove.yml')
.exclude('tools/manual_api_docs/blocks/*.md')
.exclude('tools/manual_api_docs/elements/*.md'),
[
contains_any_globs(files.exclude('.pullapprove.yml'), [
'{*,.*}',
'.agent/**/{*,.*}',
'.devcontainer/**/{*,.*}',
'.github/**/{*,.*}',
'.husky/**/{*,.*}',
@@ -340,7 +340,6 @@ groups:
- author not in ["angular-robot"]
reviewers:
users:
- alan-agius4
- devversion
- josephperrott
@@ -375,9 +374,11 @@ groups:
- AndrewKushnir
- ~alxhub
- atscott
- ~jelbourn
- thePunderWoman
- ~pkozlowski-opensource
- kirjs
- mmalerba
- crisbeto
- devversion
- JeanMeche
@@ -411,8 +412,10 @@ groups:
- AndrewKushnir
- atscott
- kirjs
- ~jelbourn
- thePunderWoman
- ~pkozlowski-opensource
- mmalerba
- ~amishne
- ~leonsenft
- ~mattrbeck
@@ -421,24 +424,6 @@ groups:
required: 1 # Require that 1 person approve
reviewed_for: required
# =========================================================
# Agent Skills
# =========================================================
agent-skills:
<<: *defaults
conditions:
- >
contains_any_globs(files, [
'skills/{*,.*}',
'skills/**/{*,.*}',
])
reviewers:
users:
- marktechson
- kirjs
- ~JeanMeche
- ~dgp1130
####################################################################################
# Special Cases
####################################################################################
@@ -459,6 +444,7 @@ groups:
- AndrewKushnir
- andrewseguin
- dgp1130
- ~jelbourn
- thePunderWoman
- josephperrott
@@ -481,6 +467,10 @@ groups:
- thePunderWoman # Jessica Janiuk
- AndrewKushnir # Andrew Kushnir
- atscott # Andrew Scott
labels:
pending: 'requires: TGP'
approved: 'requires: TGP'
rejected: 'requires: TGP'
# External team required reviews
primitives-shared:
@@ -498,6 +488,10 @@ groups:
- tbondwilkinson # Tom Wilkinson
- rahatarmanahmed # Rahat Ahmed
- ENAML # Ethan Cline
labels:
pending: 'requires: TGP'
approved: 'requires: TGP'
rejected: 'requires: TGP'
####################################################################################
# Override managed result groups
@@ -520,6 +514,7 @@ groups:
reviewers:
users:
- alxhub
- jelbourn
- josephperrott
reviews:
request: 0
+2 -19
View File
@@ -1,13 +1,9 @@
---
trigger: always_on
---
This is the source code for the Angular framework. This guide outlines standard practices for AI agents working in this repository.
## Environment
- Use `pnpm` for package management.
- Use `pnpm bazel test //target` to run tests.
- Use `pnpm bazel test //target` to run tests. Do not use `ng test`, or just `bazel`
## Key Documentation
@@ -15,20 +11,7 @@ This is the source code for the Angular framework. This guide outlines standard
- [Coding Standards](contributing-docs/coding-standards.md): style guide for TypeScript and other files.
- [Commit Guidelines](contributing-docs/commit-message-guidelines.md): format for commit messages and PR titles.
## Testing
- **Zoneless & Async-First:** Assume a zoneless environment where state changes schedule updates asynchronously.
- **Do NOT** use `fixture.detectChanges()` to manually trigger updates.
- **ALWAYS** use the "Act, Wait, Assert" pattern:
1. **Act:** Update state or perform an action.
2. **Wait:** `await fixture.whenStable()` to allow the framework to process the scheduled update.
3. **Assert:** Verify the output.
- To keep tests fast, minimize the need for waiting:
- Use `useAutoTick()` (from `packages/private/testing/src/utils.ts`) to fast-forward time via the mock clock.
- When waiting is necessary, use real async tests (`it('...', async () => { ... })`) along with:
- `await timeout(ms)` (from `packages/private/testing/src/utils.ts`) to wait a specific number of milliseconds.
- `await fixture.whenStable()` to wait for framework stability.
## Pull Requests
- Use the `gh` CLI (GitHub CLI) for creating and managing pull requests.
- Avoid using browser tools for PR operations when possible to maintain workflow efficiency.
-162
View File
@@ -1,151 +1,3 @@
<a name="21.2.3"></a>
# 21.2.3 (2026-03-11)
### core
| Commit | Type | Description |
| -- | -- | -- |
| [62a97f7e4b](https://github.com/angular/angular/commit/62a97f7e4b896b4b03a1ef25764db387ffecebe1) | fix | ensure definitions compile |
| [21b1c3b2ee](https://github.com/angular/angular/commit/21b1c3b2ee2c8423782b111b93bd60eb6b453259) | fix | include signal debug names in their `toString()` representation |
| [224e60ecb1](https://github.com/angular/angular/commit/224e60ecb1b90115baa702f1c06edc1d64d86187) | fix | sanitize translated attribute bindings with interpolations |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.2"></a>
# 21.2.2 (2026-03-09)
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [1df1697c6e](https://github.com/angular/angular/commit/1df1697c6e3a6b1d302f7692b495146943faa12f) | fix | prevent mutation of children array in RecursiveVisitor |
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [c822bf8e76](https://github.com/angular/angular/commit/c822bf8e76611afde332b6625f5e7bae2fe9c3f3) | fix | always parenthesize object literals in TCB |
| [05d022d5e6](https://github.com/angular/angular/commit/05d022d5e61cca7ac90d5b2b2ba3fc738b364ad9) | fix | ignore generated ngDevMode signal branch for code coverage |
### forms
| Commit | Type | Description |
| -- | -- | -- |
| [670d1660c4](https://github.com/angular/angular/commit/670d1660c40504e3f55e094c3ebbcccad14163f3) | feat | add 'blur' option to debounce rule |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.1"></a>
# 21.2.1 (2026-03-04)
### core
| Commit | Type | Description |
| -- | -- | -- |
| [e2e9a9a531](https://github.com/angular/angular/commit/e2e9a9a531c9e9a69701e549f28354cc5d5edd77) | fix | adds transfer cache to httpResource to fix hydration |
| [b4ec3cc4e4](https://github.com/angular/angular/commit/b4ec3cc4e41f2948ad0830eb14aa05d14fa3a9ed) | fix | prevent child animation elements from being orphaned |
| [e923d88398](https://github.com/angular/angular/commit/e923d8839868c79989502ab3503e13d93c78516a) | fix | Prevent removal of elements during drag and drop |
### http
| Commit | Type | Description |
| -- | -- | -- |
| [277ade97ac](https://github.com/angular/angular/commit/277ade97ac2a3a7f2a5b513acaa93e7663cdc55f) | fix | correctly cache blob responses in transfer cache ([#67002](https://github.com/angular/angular/pull/67002)) |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.2.0"></a>
# 21.2.0 (2026-02-25)
### common
| Commit | Type | Description |
| -- | -- | -- |
| [18003a33bb](https://github.com/angular/angular/commit/18003a33bb0d6bb09def8a0e5939fa24069696eb) | feat | add an 'outlet' injector option for ngTemplateOutlet |
| [8bbe6dc46c](https://github.com/angular/angular/commit/8bbe6dc46c9dc13bafa81a60c7613b84b5ca3761) | feat | Add Location strategies to manage trailing slash on write |
| [51cc914807](https://github.com/angular/angular/commit/51cc91480761b7275c15b5600381207f8ca00ee5) | feat | support height in ImageLoaderConfig and built-in loaders |
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [72534e2a34](https://github.com/angular/angular/commit/72534e2a3458df4e1bb097973872f00bbb92be42) | feat | Add support for the `instanceof` binary operator |
| [95b3f37d4a](https://github.com/angular/angular/commit/95b3f37d4a7d9a38f616d56df746dfcda3c2139b) | feat | Exhaustive checks for switch blocks |
| [04ba09a8d9](https://github.com/angular/angular/commit/04ba09a8d9454013bebdd643eacb737642161952) | feat | support `AstVisitor.visitEmptyExpr()` |
| [ce80136e7b](https://github.com/angular/angular/commit/ce80136e7b9f0024d49fce835cffa024c4505855) | fix | optimize away unnecessary restore/reset view calls |
| [3242a61bae](https://github.com/angular/angular/commit/3242a61bae02253d13abb510b666376c665e61ac) | fix | variable counter visiting some expressions twice |
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [473dd3e1cb](https://github.com/angular/angular/commit/473dd3e1cbd4fe3fa88ae4d5358eee35c11acb1b) | fix | attach source spans to object literal keys in TCB |
| [a904d9f77b](https://github.com/angular/angular/commit/a904d9f77b56feab407f75f8d0527fa512d5dafb) | fix | support nested component declaration |
| [2ea6dfc6c9](https://github.com/angular/angular/commit/2ea6dfc6c9ca11e96a2654510c980419899f8d04) | fix | update diagnostic to flag no-op arrow functions in listeners |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [8d5210c9fe](https://github.com/angular/angular/commit/8d5210c9fedd8abdd810d7a89ec7ee9a1234f5c1) | feat | add ChangeDetectionStrategy.Eager alias for Default |
| [92d2498910](https://github.com/angular/angular/commit/92d2498910caed06c182b67e39726e1441418698) | feat | add host node to DeferBlockData ([#66546](https://github.com/angular/angular/pull/66546)) |
| [ea2016a6dc](https://github.com/angular/angular/commit/ea2016a6dce58f95ecab7c773d5dcde274354e1a) | feat | add support for nested animations |
| [81cabc1477](https://github.com/angular/angular/commit/81cabc14777a3b4966c29d60e1505aca8c29b71c) | feat | add support for TypeScript 6 |
| [1ba9b7ac50](https://github.com/angular/angular/commit/1ba9b7ac5001b315cc9df78c518964dbf479d647) | feat | resource composition via snapshots |
| [d9923b72a2](https://github.com/angular/angular/commit/d9923b72a20972ba6bf728d78f1afac6936ade18) | feat | support arrow functions in expressions |
| [a7e8abbb7e](https://github.com/angular/angular/commit/a7e8abbb7e738ba338c3f50c76934c99925954e5) | fix | correctly handle SkipSelf when resolving from embedded view injector |
| [0806ee3826](https://github.com/angular/angular/commit/0806ee38269b664f535e10d4d501b88370d3b44c) | fix | prevent animated element duplication with dynamic components in zoneless mode |
| [ed78fa05c7](https://github.com/angular/angular/commit/ed78fa05c710ebafb355ae00a85b190a118b6cc4) | fix | Remove note to skip arrow functions in best practices |
### forms
| Commit | Type | Description |
| -- | -- | -- |
| [f56bb07d83](https://github.com/angular/angular/commit/f56bb07d83a015b0ac12e74fdb0cf1550ff36b97) | feat | add field param to submit action and onInvalid |
| [ba009b6031](https://github.com/angular/angular/commit/ba009b603119299a03f9d844f93882d42d47d150) | feat | add form directive |
| [22afbb2f36](https://github.com/angular/angular/commit/22afbb2f36be89c2ae575df343571a918dec5985) | feat | add parsing support to native inputs ([#66917](https://github.com/angular/angular/pull/66917)) |
| [95c386469c](https://github.com/angular/angular/commit/95c386469c7a2f09dd731601c2061bdb10d25717) | feat | Add passing focus options to form field |
| [95ecce8334](https://github.com/angular/angular/commit/95ecce8334299defe55fb2b74264e5258ffd137c) | feat | allow setting submit options at form-level |
| [ebae211add](https://github.com/angular/angular/commit/ebae211add37700858adeb8fc5d87bf503a59721) | feat | introduce parse errors in signal forms |
| [3937afc316](https://github.com/angular/angular/commit/3937afc3167ce409eebb06d91d5fb122eea4e33d) | feat | introduce SignalFormControl for Reactive Forms compatibility |
| [30f0914754](https://github.com/angular/angular/commit/30f09147545b67185f93efb9796e37c1db76733a) | feat | support binding null to number input ([#66917](https://github.com/angular/angular/pull/66917)) |
| [dd208ca259](https://github.com/angular/angular/commit/dd208ca2595258fcd1e289374f812ce0b56c7011) | feat | update submit function to accept options object |
| [27397b3f4f](https://github.com/angular/angular/commit/27397b3f4f3182ce00d6e2f8690285c316e2a274) | fix | clear parse errors when model updates ([#66917](https://github.com/angular/angular/pull/66917)) |
| [63d8005703](https://github.com/angular/angular/commit/63d80057039928b3e878b59c1fe6b93ef1c6b701) | fix | preserve custom-control focus context in signal forms |
| [631f60d1f9](https://github.com/angular/angular/commit/631f60d1f9be72cb68330308a6ff18cc195babb8) | fix | preserve parse errors when parse returns value |
| [adfb83146b](https://github.com/angular/angular/commit/adfb83146b0c149734f43961563b389e00cc1d85) | fix | simplify design of parse errors |
| [fb05fc86d0](https://github.com/angular/angular/commit/fb05fc86d0f12ffafd94c7c1420118d8a79f7e59) | fix | sort error summary by DOM order |
| [567f292e8e](https://github.com/angular/angular/commit/567f292e8e0f9d2b5ddebadfa1c6d6dd6c456f39) | fix | support custom controls as host directives |
| [bdfb60f3e3](https://github.com/angular/angular/commit/bdfb60f3e33065e047183dc1890c36e527e2b304) | fix | use consistent error format returned from parse |
| [d75046bc09](https://github.com/angular/angular/commit/d75046bc091699bbadcb5f2032be627e983ee6fa) | fix | warn when showing hidden field state |
### language-server
| Commit | Type | Description |
| -- | -- | -- |
| [ebc90c26f5](https://github.com/angular/angular/commit/ebc90c26f5ff1ba1e0ca9b775a44e301ebfb9473) | feat | Add completions and hover info for inline styles |
| [26fd0839c3](https://github.com/angular/angular/commit/26fd0839c32d2ebeaa5e3ecc10ed70ab9ca17749) | feat | Add folding range support for inline styles |
| [573aadef7e](https://github.com/angular/angular/commit/573aadef7eb8b6b5e83b82a16f95d2a556f27c01) | feat | Add quick info for inline styles |
| [6fb39d9b62](https://github.com/angular/angular/commit/6fb39d9b62cbb634e95ec00fe5ef85d84da3bdbd) | feat | Support client-side file watching via `onDidChangeWatchedFiles` |
### language-service
| Commit | Type | Description |
| -- | -- | -- |
| [496967e7b1](https://github.com/angular/angular/commit/496967e7b13dfe1ebdde69724cd62880914beb60) | feat | add JSON schema for angularCompilerOptions |
| [8c21866f49](https://github.com/angular/angular/commit/8c21866f49ff74344551395ae0a5df1841d54c0d) | feat | add linked editing ranges for HTML tag synchronization |
| [d2137928e8](https://github.com/angular/angular/commit/d2137928e8f075527016a3c011dd8efc4d4e1ebd) | perf | use lightweight project warmup for Angular analysis |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [b51bab583d](https://github.com/angular/angular/commit/b51bab583d84e38f16dea489e4119edc34e2a491) | feat | Add partial ActivatedRouteSnapshot information to `canMatch` params |
| [cf9620f7d0](https://github.com/angular/angular/commit/cf9620f7d072897f13b7f281b7bca6f51f69cfd0) | feat | Make match options optional in isActive |
| [907a94dcec](https://github.com/angular/angular/commit/907a94dcec2926a5c7d0c4d36249bd62e31a2ae3) | feat | Update `IsActiveMatchOptions` APIs to accept a Partial |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.1.5"></a>
# 21.1.5 (2026-02-18)
No user facing changes in this release
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.1.4"></a>
# 21.1.4 (2026-02-11)
### compiler
| Commit | Type | Description |
| -- | -- | -- |
| [caab23dfe6](https://github.com/angular/angular/commit/caab23dfe6acf06c3b859af091f5e078b08f1c4c) | fix | add geolocation element to schema |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [2b99eaa019](https://github.com/angular/angular/commit/2b99eaa019b5551a2e2fcf9ff8cd0a796e1e857b) | fix | capture animation dependencies eagerly to avoid destroyed injector |
| [d6aeac504c](https://github.com/angular/angular/commit/d6aeac504c6181f15e5d8afdca3d9c3e3b32652c) | fix | Fix flakey test due to document injection |
### forms
| Commit | Type | Description |
| -- | -- | -- |
| [0d1acd0165](https://github.com/angular/angular/commit/0d1acd0165511b57ce853f29486d9b92d0215959) | feat | support signal-based schemas in validateStandardSchema |
### http
| Commit | Type | Description |
| -- | -- | -- |
| [3905015ccc](https://github.com/angular/angular/commit/3905015ccc53399a606dd8e4f3c4d0cce628a08e) | fix | correctly parse ArrayBuffer and Blob in transfer cache |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.1.3"></a>
# 21.1.3 (2026-02-04)
### core
@@ -256,20 +108,6 @@ No user facing changes in this release
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.0.9"></a>
# 21.0.9 (2026-01-14)
### forms
| Commit | Type | Description |
| -- | -- | -- |
| [82d556a8fb](https://github.com/angular/angular/commit/82d556a8fb38cd2024e3d098c55254305ba12b6b) | fix | Ensure the control instruction comes after the other bindings |
| [0055f3cc79](https://github.com/angular/angular/commit/0055f3cc79f387b8dec6ce5e1a33fad5486f9341) | fix | Rename signal form [field] to [formField] |
### migrations
| Commit | Type | Description |
| -- | -- | -- |
| [e4bfa5c9e7](https://github.com/angular/angular/commit/e4bfa5c9e7feec48d3c4e9425a21a2ccf6532bdb) | fix | prevent duplicate imports in common-to-standalone migration |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.0.8"></a>
# 21.0.8 (2026-01-08)
### core
+21 -20
View File
@@ -6,26 +6,26 @@ module(
bazel_dep(name = "rules_pkg", version = "1.2.0")
bazel_dep(name = "rules_nodejs", version = "6.7.3")
bazel_dep(name = "aspect_rules_ts", version = "3.8.5")
bazel_dep(name = "aspect_rules_ts", version = "3.8.3")
bazel_dep(name = "aspect_rules_js", version = "2.9.2")
bazel_dep(name = "aspect_rules_esbuild", version = "0.25.0")
bazel_dep(name = "aspect_rules_jasmine", version = "2.0.4")
bazel_dep(name = "aspect_rules_jasmine", version = "2.0.2")
bazel_dep(name = "aspect_rules_rollup", version = "2.0.1")
bazel_dep(name = "bazel_skylib", version = "1.9.0")
bazel_dep(name = "aspect_bazel_lib", version = "2.22.5")
bazel_dep(name = "tar.bzl", version = "0.9.0")
bazel_dep(name = "yq.bzl", version = "0.3.5")
bazel_dep(name = "tar.bzl", version = "0.8.1")
bazel_dep(name = "yq.bzl", version = "0.3.4")
bazel_dep(name = "rules_angular")
git_override(
module_name = "rules_angular",
commit = "d746c4f75e42cffe389d1ab077f4639be2bc78d1",
remote = "https://github.com/angular/rules_angular.git",
commit = "e975faa54a52caa45ff932ec806292ce0988d8d4",
remote = "https://github.com/devversion/rules_angular.git",
)
bazel_dep(name = "devinfra")
git_override(
module_name = "devinfra",
commit = "e4dcda13e469f50a6bd92667e2ded8b98173bf2c",
commit = "3bec1182014f8cd3201bfc7d2bc6121658b9e0cd",
remote = "https://github.com/angular/dev-infra.git",
)
@@ -33,14 +33,14 @@ bazel_dep(name = "rules_sass")
git_override(
module_name = "rules_sass",
commit = "1184a80751a21af8348f308abc5b38a41f26850e",
remote = "https://github.com/angular/rules_sass.git",
remote = "https://github.com/devversion/rules_sass.git",
)
bazel_dep(name = "rules_browsers")
git_override(
module_name = "rules_browsers",
commit = "8231142fc5516323b040883bf774ba8362645387",
remote = "https://github.com/angular/rules_browsers.git",
commit = "5f0e50d17d15c70e6ab5546f1659338ae94c4072",
remote = "https://github.com/devversion/rules_browsers.git",
)
yq = use_extension("@yq.bzl//yq:extensions.bzl", "yq")
@@ -49,15 +49,15 @@ use_repo(yq, "yq_toolchains")
node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node")
node.toolchain(
node_repositories = {
"22.22.1-darwin_arm64": ("node-v22.22.1-darwin-arm64.tar.gz", "node-v22.22.1-darwin-arm64", "679ad4966339e4ef4900f57996714864e4211b898825bb840c3086c419fbcef2"),
"22.22.1-darwin_amd64": ("node-v22.22.1-darwin-x64.tar.gz", "node-v22.22.1-darwin-x64", "07b13722d558790fca20bb1ecf61bde24b7a4863111f7be77fc57251a407359a"),
"22.22.1-linux_arm64": ("node-v22.22.1-linux-arm64.tar.xz", "node-v22.22.1-linux-arm64", "0f3550d58d45e5d3cf7103d9e3f69937f09fe82fb5dd474c66a5d816fa58c9ee"),
"22.22.1-linux_ppc64le": ("node-v22.22.1-linux-ppc64le.tar.xz", "node-v22.22.1-linux-ppc64le", "a65a44cf0224505f052b90357b763dbc1ea9148f4f5f2284f0596cf2000f819b"),
"22.22.1-linux_s390x": ("node-v22.22.1-linux-s390x.tar.xz", "node-v22.22.1-linux-s390x", "e08b3a73d0bd840e008f589e4be4a2ef3d4a0c59015f4f20a04ed7fc968042a2"),
"22.22.1-linux_amd64": ("node-v22.22.1-linux-x64.tar.xz", "node-v22.22.1-linux-x64", "9a6bc82f9b491279147219f6a18add1e18424dce90d41d2a5fcd69d4924ba3aa"),
"22.22.1-windows_amd64": ("node-v22.22.1-win-x64.zip", "node-v22.22.1-win-x64", "877cb93829e14fffbbc7903e7d8037336c9a79f3ea43c5d0b8c2379b79da56de"),
"22.22.0-darwin_arm64": ("node-v22.22.0-darwin-arm64.tar.gz", "node-v22.22.0-darwin-arm64", "5ed4db0fcf1eaf84d91ad12462631d73bf4576c1377e192d222e48026a902640"),
"22.22.0-darwin_amd64": ("node-v22.22.0-darwin-x64.tar.gz", "node-v22.22.0-darwin-x64", "5ea50c9d6dea3dfa3abb66b2656f7a4e1c8cef23432b558d45fb538c7b5dedce"),
"22.22.0-linux_arm64": ("node-v22.22.0-linux-arm64.tar.xz", "node-v22.22.0-linux-arm64", "1bf1eb9ee63ffc4e5d324c0b9b62cf4a289f44332dfef9607cea1a0d9596ba6f"),
"22.22.0-linux_ppc64le": ("node-v22.22.0-linux-ppc64le.tar.xz", "node-v22.22.0-linux-ppc64le", "d83b9957431cc18e1fc143a4b99f89cde7b8a18f53ef392231b4336afd058865"),
"22.22.0-linux_s390x": ("node-v22.22.0-linux-s390x.tar.xz", "node-v22.22.0-linux-s390x", "5aa0e520689448c4233e8d73f284e8e0634fdcd32b479735698494be5641f3e4"),
"22.22.0-linux_amd64": ("node-v22.22.0-linux-x64.tar.xz", "node-v22.22.0-linux-x64", "9aa8e9d2298ab68c600bd6fb86a6c13bce11a4eca1ba9b39d79fa021755d7c37"),
"22.22.0-windows_amd64": ("node-v22.22.0-win-x64.zip", "node-v22.22.0-win-x64", "c97fa376d2becdc8863fcd3ca2dd9a83a9f3468ee7ccf7a6d076ec66a645c77a"),
},
node_version = "22.22.1",
node_version = "22.22.0",
)
use_repo(node, "nodejs_toolchains")
use_repo(node, "nodejs_darwin_amd64")
@@ -71,8 +71,8 @@ use_repo(node, "nodejs_windows_amd64")
pnpm = use_extension("@aspect_rules_js//npm:extensions.bzl", "pnpm")
pnpm.pnpm(
name = "pnpm",
pnpm_version = "10.32.0",
pnpm_version_integrity = "sha512-myY0uz/tVgHDNjPy2SWT9QYnCjljuMUdKy1qgo2mFc5One6++WFMzrvBOsjTwPnJzM61g8achXhDb6R327INcA==",
pnpm_version = "10.28.2",
pnpm_version_integrity = "sha512-QYcvA3rSL3NI47Heu69+hnz9RI8nJtnPdMCPGVB8MdLI56EVJbmD/rwt9kC1Q43uYCPrsfhO1DzC1lTSvDJiZA==",
)
use_repo(pnpm, "pnpm")
@@ -83,6 +83,7 @@ npm.npm_translate_lock(
"//:.pnpmfile.cjs",
"//:package.json",
"//:pnpm-workspace.yaml",
"//:tools/pnpm-patches/dagre-d3-es+7.0.11.patch",
"//adev:package.json",
"//adev/shared-docs:package.json",
"//adev/shared-docs/pipeline/api-gen:package.json",
+286 -288
View File
@@ -22,8 +22,8 @@
"https://bcr.bazel.build/modules/aspect_bazel_lib/2.9.3/MODULE.bazel": "66baf724dbae7aff4787bf2245cc188d50cb08e07789769730151c0943587c14",
"https://bcr.bazel.build/modules/aspect_rules_esbuild/0.25.0/MODULE.bazel": "5fef5ec709c837312823f9bcf0f276661e2cb48ad52f17c4e01176bbf1e9bf58",
"https://bcr.bazel.build/modules/aspect_rules_esbuild/0.25.0/source.json": "5e42968c6d23ab8bd95c02634b16864d866334347827cb6a8425b86c11cc4363",
"https://bcr.bazel.build/modules/aspect_rules_jasmine/2.0.4/MODULE.bazel": "fbb819eb8b7e5d7f67fdd38f7cecb413e287594cd666ce192c72c8828527775a",
"https://bcr.bazel.build/modules/aspect_rules_jasmine/2.0.4/source.json": "81ffb708333cd98ec3c0b4cc004f4d5cf92a16914b5196a2892c45141bba7cff",
"https://bcr.bazel.build/modules/aspect_rules_jasmine/2.0.2/MODULE.bazel": "45f054400ff242c4433f6d7f20f6123a9a72739cb7a1f44247d738db1644f46c",
"https://bcr.bazel.build/modules/aspect_rules_jasmine/2.0.2/source.json": "3ed399a5654259a822448f9cdbf21f6c738f16ccd7f89249c7507e374cbdd1e3",
"https://bcr.bazel.build/modules/aspect_rules_js/2.0.0/MODULE.bazel": "b45b507574aa60a92796e3e13c195cd5744b3b8aff516a9c0cb5ae6a048161c5",
"https://bcr.bazel.build/modules/aspect_rules_js/2.4.2/MODULE.bazel": "0d01db38b96d25df7ed952a5e96eac4b3802723d146961974bf020f6dd07591d",
"https://bcr.bazel.build/modules/aspect_rules_js/2.6.2/MODULE.bazel": "ed2a871f4ab8fbde0cab67c425745069d84ea64b64313fa1a2954017326511f5",
@@ -33,8 +33,8 @@
"https://bcr.bazel.build/modules/aspect_rules_rollup/2.0.1/source.json": "2fe8ac1ccb4de74bf884761e070010280b272d94e3997205b361b91c75409726",
"https://bcr.bazel.build/modules/aspect_rules_ts/3.6.3/MODULE.bazel": "d09db394970f076176ce7bab5b5fa7f0d560fd4f30b8432ea5e2c2570505b130",
"https://bcr.bazel.build/modules/aspect_rules_ts/3.7.0/MODULE.bazel": "5aace216caf88638950ef061245d23c36f57c8359e56e97f02a36f70bb09c50f",
"https://bcr.bazel.build/modules/aspect_rules_ts/3.8.5/MODULE.bazel": "bcf8f0b6b9375f0f74451e2f70671efae9bb366acef8fdc04675305eaf137f06",
"https://bcr.bazel.build/modules/aspect_rules_ts/3.8.5/source.json": "fa35e43f6359f654e4b70ce55efdf280d0b06c0b3ef9fc0b06ba52327a0e6311",
"https://bcr.bazel.build/modules/aspect_rules_ts/3.8.3/MODULE.bazel": "a26c28ebcd0c0d50ab0708ac21fa48bd2dced3a4dad4c31a2fa48588b42ad762",
"https://bcr.bazel.build/modules/aspect_rules_ts/3.8.3/source.json": "d17304791281168c42c5532b4b9e01dfb4bdb42d7bf784597b75f401211efc63",
"https://bcr.bazel.build/modules/aspect_tools_telemetry/0.2.3/MODULE.bazel": "20f53b145f40957a51077ae90b37b7ce83582a1daf9350349f0f86179e19dd0d",
"https://bcr.bazel.build/modules/aspect_tools_telemetry/0.2.6/MODULE.bazel": "cafb8781ad591bc57cc765dca5fefab08cf9f65af363d162b79d49205c7f8af7",
"https://bcr.bazel.build/modules/aspect_tools_telemetry/0.2.8/MODULE.bazel": "aa975a83e72bcaac62ee61ab12b788ea324a1d05c4aab28aadb202f647881679",
@@ -77,8 +77,7 @@
"https://bcr.bazel.build/modules/buildozer/7.1.2/MODULE.bazel": "2e8dd40ede9c454042645fd8d8d0cd1527966aa5c919de86661e62953cd73d84",
"https://bcr.bazel.build/modules/buildozer/7.1.2/source.json": "c9028a501d2db85793a6996205c8de120944f50a0d570438fcae0457a5f9d1f8",
"https://bcr.bazel.build/modules/gawk/5.3.2.bcr.1/MODULE.bazel": "cdf8cbe5ee750db04b78878c9633cc76e80dcf4416cbe982ac3a9222f80713c8",
"https://bcr.bazel.build/modules/gawk/5.3.2.bcr.3/MODULE.bazel": "f1b7bb2dd53e8f2ef984b39485ec8a44e9076dda5c4b8efd2fb4c6a6e856a31d",
"https://bcr.bazel.build/modules/gawk/5.3.2.bcr.3/source.json": "ebe931bfe362e4b41e59ee00a528db6074157ff2ced92eb9e970acab2e1089c9",
"https://bcr.bazel.build/modules/gawk/5.3.2.bcr.1/source.json": "fa7b512dfcb5eafd90ce3959cf42a2a6fe96144ebbb4b3b3928054895f2afac2",
"https://bcr.bazel.build/modules/google_benchmark/1.8.2/MODULE.bazel": "a70cf1bba851000ba93b58ae2f6d76490a9feb74192e57ab8e8ff13c34ec50cb",
"https://bcr.bazel.build/modules/googletest/1.11.0/MODULE.bazel": "3a83f095183f66345ca86aa13c58b59f9f94a2f81999c093d4eeaa2d262d12f4",
"https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/MODULE.bazel": "22c31a561553727960057361aa33bf20fb2e98584bc4fec007906e27053f80c6",
@@ -127,7 +126,6 @@
"https://bcr.bazel.build/modules/rules_cc/0.1.1/MODULE.bazel": "2f0222a6f229f0bf44cd711dc13c858dad98c62d52bd51d8fc3a764a83125513",
"https://bcr.bazel.build/modules/rules_cc/0.2.16/MODULE.bazel": "9242fa89f950c6ef7702801ab53922e99c69b02310c39fb6e62b2bd30df2a1d4",
"https://bcr.bazel.build/modules/rules_cc/0.2.16/source.json": "d03d5cde49376d87e14ec14b666c56075e5e3926930327fd5d0484a1ff2ac1cc",
"https://bcr.bazel.build/modules/rules_cc/0.2.4/MODULE.bazel": "1ff1223dfd24f3ecf8f028446d4a27608aa43c3f41e346d22838a4223980b8cc",
"https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6",
"https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8",
"https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e",
@@ -200,14 +198,14 @@
"https://bcr.bazel.build/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216",
"https://bcr.bazel.build/modules/tar.bzl/0.2.1/MODULE.bazel": "52d1c00a80a8cc67acbd01649e83d8dd6a9dc426a6c0b754a04fe8c219c76468",
"https://bcr.bazel.build/modules/tar.bzl/0.5.1/MODULE.bazel": "7c2eb3dcfc53b0f3d6f9acdfd911ca803eaf92aadf54f8ca6e4c1f3aee288351",
"https://bcr.bazel.build/modules/tar.bzl/0.9.0/MODULE.bazel": "452a22d7f02b1c9d7a22ab25edf20f46f3e1101f0f67dc4bfbf9a474ddf02445",
"https://bcr.bazel.build/modules/tar.bzl/0.9.0/source.json": "c732760a374831a2cf5b08839e4be75017196b4d796a5aa55235272ee17cd839",
"https://bcr.bazel.build/modules/tar.bzl/0.8.1/MODULE.bazel": "6ffe8907ed4c555bc94bd35a5a01411cc4470c6dace84f9cf487815409e077d1",
"https://bcr.bazel.build/modules/tar.bzl/0.8.1/source.json": "835f83b482facf6205ad8708cf2b2f6524d1d7b1075a90fe9bb540da761d6d2e",
"https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43",
"https://bcr.bazel.build/modules/yq.bzl/0.1.1/MODULE.bazel": "9039681f9bcb8958ee2c87ffc74bdafba9f4369096a2b5634b88abc0eaefa072",
"https://bcr.bazel.build/modules/yq.bzl/0.2.0/MODULE.bazel": "6f3a675677db8885be4d607fde14cc51829715e3a879fb016eb9bf336786ce6d",
"https://bcr.bazel.build/modules/yq.bzl/0.3.2/MODULE.bazel": "0384efa70e8033d842ea73aa4b7199fa099709e236a7264345c03937166670b6",
"https://bcr.bazel.build/modules/yq.bzl/0.3.5/MODULE.bazel": "130c603e54be717bdf84100210f06598a0d2b4b4e01888fb01b70f50f41767ec",
"https://bcr.bazel.build/modules/yq.bzl/0.3.5/source.json": "1ae7bdc03cb26aaa8bd2bceadf65e90d90f0b2d03008ba9a0564da2e21396c39",
"https://bcr.bazel.build/modules/yq.bzl/0.3.4/MODULE.bazel": "d3a270662f5d766cd7229732d65a5a5bc485240c3007343dd279edfb60c9ae27",
"https://bcr.bazel.build/modules/yq.bzl/0.3.4/source.json": "786dafdc2843722da3416e4343ee1a05237227f068590779a6e8496a2064c0f9",
"https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0",
"https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca",
"https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/source.json": "22bc55c47af97246cfc093d0acf683a7869377de362b5d1c552c2c2e16b7a806",
@@ -217,7 +215,7 @@
"moduleExtensions": {
"@@aspect_rules_esbuild+//esbuild:extensions.bzl%esbuild": {
"general": {
"bzlTransitiveDigest": "fzdwsWU1Px4bYL57Cw1rvU/mgOqB7f0AF51JxT5kAs8=",
"bzlTransitiveDigest": "c4i5gawrp4Au9UMb55EzQCePYwkrFqD9tFBN7GdHG5g=",
"usagesDigest": "ToTaCONCN/E05krnHXLM1kpV1zrHNxHrGpUip973II4=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
@@ -418,8 +416,8 @@
},
"@@aspect_rules_js+//npm:extensions.bzl%pnpm": {
"general": {
"bzlTransitiveDigest": "9Zmqluz9ltvCoc6pskOslTkQpkH6CIINjWVkgam9aa4=",
"usagesDigest": "pLuUB7Fmb40xWdK1ynAXIWXeucWkJ0R9x2nDjqKhbq4=",
"bzlTransitiveDigest": "HC+l+mTivq1p/KbcVQ+iV5QwYR+oKESJh827FY68SH8=",
"usagesDigest": "vXOmvUr0fqA3q7dqX4ag6bG81ZACRs8u7NWugFlQ2j8=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
@@ -428,11 +426,11 @@
"repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_rule",
"attributes": {
"package": "pnpm",
"version": "10.32.0",
"version": "10.28.2",
"root_package": "",
"link_workspace": "",
"link_packages": {},
"integrity": "sha512-myY0uz/tVgHDNjPy2SWT9QYnCjljuMUdKy1qgo2mFc5One6++WFMzrvBOsjTwPnJzM61g8achXhDb6R327INcA==",
"integrity": "sha512-QYcvA3rSL3NI47Heu69+hnz9RI8nJtnPdMCPGVB8MdLI56EVJbmD/rwt9kC1Q43uYCPrsfhO1DzC1lTSvDJiZA==",
"url": "",
"commit": "",
"patch_args": [
@@ -455,7 +453,7 @@
"repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_links",
"attributes": {
"package": "pnpm",
"version": "10.32.0",
"version": "10.28.2",
"dev": false,
"root_package": "",
"link_packages": {},
@@ -572,10 +570,10 @@
},
"@@aspect_rules_ts+//ts:extensions.bzl%ext": {
"general": {
"bzlTransitiveDigest": "6NyLUdrb79stFKm8oWIBK2q65cT5Upbr8IR2Qwsywu8=",
"usagesDigest": "ZXlhH7TrNEERA0ibuRRxAjYv2fVuXoFGNd3v+0UK1/g=",
"bzlTransitiveDigest": "gyALvLdd/1AuLPFXhzNeabk2DMLHf32oxn8qKfEK8UA=",
"usagesDigest": "BIPOUwqzuuYqLOw/0p3h5GwHIDeBVXvNcxNoG5YlsHQ=",
"recordedFileInputs": {
"@@rules_browsers+//package.json": "772d873d450a539e2133635aeb5e63744cf1cec86e6b37aeecd9267a147fb0d7"
"@@rules_browsers+//package.json": "84dc1ba9b1c667a25894e97218bd8f247d54f24bb694efb397a881be3c06a4c5"
},
"recordedDirentsInputs": {},
"envVariables": {},
@@ -639,7 +637,7 @@
"@@aspect_tools_telemetry+//:extension.bzl%telemetry": {
"general": {
"bzlTransitiveDigest": "cl5A2O84vDL6Tt+Qga8FCj1DUDGqn+e7ly5rZ+4xvcc=",
"usagesDigest": "fkarigrHiiNAt/BwClRuO51QOEo6226baK6LoK/IWdU=",
"usagesDigest": "m4Fa2faQCHxeGwDkOwASSCEydzPyBW44x/rz9KgbgJQ=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
@@ -648,10 +646,10 @@
"repoRuleId": "@@aspect_tools_telemetry+//:extension.bzl%tel_repository",
"attributes": {
"deps": {
"aspect_rules_ts": "3.8.5",
"aspect_rules_ts": "3.8.3",
"aspect_rules_js": "2.9.2",
"aspect_rules_esbuild": "0.25.0",
"aspect_rules_jasmine": "2.0.4",
"aspect_rules_jasmine": "2.0.2",
"aspect_tools_telemetry": "0.3.3"
}
}
@@ -673,7 +671,7 @@
},
"@@pybind11_bazel+//:python_configure.bzl%extension": {
"general": {
"bzlTransitiveDigest": "D2/qWHU6yQFwRG7Bb+caqrYMha5avsASao2vERrxK24=",
"bzlTransitiveDigest": "c9ZWWeXeu6bctL4/SsY2otFWyeFN0JJ20+ymGyJZtWk=",
"usagesDigest": "fycyB39YnXIJkfWCIXLUKJMZzANcuLy9ZE73hRucjFk=",
"recordedFileInputs": {
"@@pybind11_bazel+//MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e"
@@ -733,8 +731,8 @@
},
"@@rules_browsers+//browsers:extensions.bzl%browsers": {
"general": {
"bzlTransitiveDigest": "Bm6fiKpWy96aLohOlLCP36ARVxRLZm/R+smhsb2HzmI=",
"usagesDigest": "FmXYJVoVJlnfUU8x8gObSvu4qWcco/9Faw61aC/wBF0=",
"bzlTransitiveDigest": "agkaLQ8wE1r/5IX6pkERzFxI/z0M42Em+ICNO6TXsVo=",
"usagesDigest": "FS7q5WaIwg3KirS3njhuPFkTIBYvDaTInVGrlzu0XL8=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
@@ -742,9 +740,9 @@
"rules_browsers_chrome_linux": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
"sha256": "1ac33f89306327af43be159c03ca4a26486de0858f42fe52394acdef50364143",
"sha256": "0a2ff0fc9eb5958b7b420f20e3968f424be7423fef89739e71565a48aa073a57",
"urls": [
"https://storage.googleapis.com/chrome-for-testing-public/147.0.7687.0/linux64/chrome-headless-shell-linux64.zip"
"https://storage.googleapis.com/chrome-for-testing-public/145.0.7586.0/linux64/chrome-headless-shell-linux64.zip"
],
"named_files": {
"CHROME-HEADLESS-SHELL": "chrome-headless-shell-linux64/chrome-headless-shell"
@@ -760,9 +758,9 @@
"rules_browsers_chrome_mac": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
"sha256": "169ff49c465cfda52931395e61861e146dfc5013e92c01ca792db5acea858d0b",
"sha256": "e6076b1201d86f74c5eab982a239d5af83e66b1aa4d780bcb792698790e01d87",
"urls": [
"https://storage.googleapis.com/chrome-for-testing-public/147.0.7687.0/mac-x64/chrome-headless-shell-mac-x64.zip"
"https://storage.googleapis.com/chrome-for-testing-public/145.0.7586.0/mac-x64/chrome-headless-shell-mac-x64.zip"
],
"named_files": {
"CHROME-HEADLESS-SHELL": "chrome-headless-shell-mac-x64/chrome-headless-shell"
@@ -778,9 +776,9 @@
"rules_browsers_chrome_mac_arm": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
"sha256": "aeaaaaa4d68193a21bed04c44ddeb1230232707b4ea1d845a92925787509cd8e",
"sha256": "b74dbcf5543d916b02d0a133e2e7c6a4de251f06733f72c2c15ea8c42213f63b",
"urls": [
"https://storage.googleapis.com/chrome-for-testing-public/147.0.7687.0/mac-arm64/chrome-headless-shell-mac-arm64.zip"
"https://storage.googleapis.com/chrome-for-testing-public/145.0.7586.0/mac-arm64/chrome-headless-shell-mac-arm64.zip"
],
"named_files": {
"CHROME-HEADLESS-SHELL": "chrome-headless-shell-mac-arm64/chrome-headless-shell"
@@ -796,9 +794,9 @@
"rules_browsers_chrome_win64": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
"sha256": "4d6d79bcbcb22084df6e3a3d3a2caff67d6c0fa488d63f0c7ec1526f9553db8c",
"sha256": "df1e612dc3b1615e182a1f11821052995913c39df37caa52699de21a68d030d2",
"urls": [
"https://storage.googleapis.com/chrome-for-testing-public/147.0.7687.0/win64/chrome-headless-shell-win64.zip"
"https://storage.googleapis.com/chrome-for-testing-public/145.0.7586.0/win64/chrome-headless-shell-win64.zip"
],
"named_files": {
"CHROME-HEADLESS-SHELL": "chrome-headless-shell-win64/chrome-headless-shell.exe"
@@ -814,9 +812,9 @@
"rules_browsers_chromedriver_linux": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
"sha256": "0607ccf6810a07ae08cac6443beac8b23f88dd53c7f1e0299e22d65f7cd2d020",
"sha256": "69c504306399d979a2766fea603c3fb9d3d87d46c75bddc9f2a049b4f636d57c",
"urls": [
"https://storage.googleapis.com/chrome-for-testing-public/147.0.7687.0/linux64/chromedriver-linux64.zip"
"https://storage.googleapis.com/chrome-for-testing-public/145.0.7586.0/linux64/chromedriver-linux64.zip"
],
"named_files": {
"CHROMEDRIVER": "chromedriver-linux64/chromedriver"
@@ -830,9 +828,9 @@
"rules_browsers_chromedriver_mac": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
"sha256": "0f512a9dd683ed4c41e609d8d02c07807497dbad3ab2f95f0d583486be7b8cff",
"sha256": "5fc9d6f594fc5f2568a15145f25116dd8e9c9a60baa8da4bb21a17650fb00e7e",
"urls": [
"https://storage.googleapis.com/chrome-for-testing-public/147.0.7687.0/mac-x64/chromedriver-mac-x64.zip"
"https://storage.googleapis.com/chrome-for-testing-public/145.0.7586.0/mac-x64/chromedriver-mac-x64.zip"
],
"named_files": {
"CHROMEDRIVER": "chromedriver-mac-x64/chromedriver"
@@ -846,9 +844,9 @@
"rules_browsers_chromedriver_mac_arm": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
"sha256": "7d6fc6d17de1733eb6739d1ea16d085c8df1568bcf9fa0d130c2784b27f38268",
"sha256": "14e92294c2c3639ca4e7d27e850588b619d698e2f8905cee368f07db2e1bf1e9",
"urls": [
"https://storage.googleapis.com/chrome-for-testing-public/147.0.7687.0/mac-arm64/chromedriver-mac-arm64.zip"
"https://storage.googleapis.com/chrome-for-testing-public/145.0.7586.0/mac-arm64/chromedriver-mac-arm64.zip"
],
"named_files": {
"CHROMEDRIVER": "chromedriver-mac-arm64/chromedriver"
@@ -862,9 +860,9 @@
"rules_browsers_chromedriver_win64": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
"sha256": "f4e9fb7bbf692fde7979b24e8d737b3cef4baafbc7a370e5d0abc4a8450fd830",
"sha256": "cf641d2e176db95bcc158cd90eafd347ad4928fa0458a5f3bfd56c6d983e70db",
"urls": [
"https://storage.googleapis.com/chrome-for-testing-public/147.0.7687.0/win64/chromedriver-win64.zip"
"https://storage.googleapis.com/chrome-for-testing-public/145.0.7586.0/win64/chromedriver-win64.zip"
],
"named_files": {
"CHROMEDRIVER": "chromedriver-win64/chromedriver.exe"
@@ -878,9 +876,9 @@
"rules_browsers_firefox_linux": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
"sha256": "f055b9c0d7346a10d22edc7f10e08679af2ea495367381ab2be9cab3ec6add97",
"sha256": "8d56f479cc398a537a60a3fa20dca92d8a41925113d3a67f534881a4e4d7e344",
"urls": [
"https://archive.mozilla.org/pub/firefox/releases/147.0/linux-x86_64/en-US/firefox-147.0.tar.xz"
"https://archive.mozilla.org/pub/firefox/releases/146.0/linux-x86_64/en-US/firefox-146.0.tar.xz"
],
"named_files": {
"FIREFOX": "firefox/firefox"
@@ -894,9 +892,9 @@
"rules_browsers_firefox_mac": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
"sha256": "48485e2068bc726e2f30cf5855fc2da1fc75c1272bc243a5394f428ffae3ba35",
"sha256": "4b1645313887972d466cd82166ea571485c2c40a167f84624e3f3ca739993cc9",
"urls": [
"https://archive.mozilla.org/pub/firefox/releases/147.0/mac/en-US/Firefox%20147.0.dmg"
"https://archive.mozilla.org/pub/firefox/releases/146.0/mac/en-US/Firefox%20146.0.dmg"
],
"named_files": {
"FIREFOX": "Firefox.app/Contents/MacOS/firefox"
@@ -910,9 +908,9 @@
"rules_browsers_firefox_mac_arm": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
"sha256": "48485e2068bc726e2f30cf5855fc2da1fc75c1272bc243a5394f428ffae3ba35",
"sha256": "4b1645313887972d466cd82166ea571485c2c40a167f84624e3f3ca739993cc9",
"urls": [
"https://archive.mozilla.org/pub/firefox/releases/147.0/mac/en-US/Firefox%20147.0.dmg"
"https://archive.mozilla.org/pub/firefox/releases/146.0/mac/en-US/Firefox%20146.0.dmg"
],
"named_files": {
"FIREFOX": "Firefox.app/Contents/MacOS/firefox"
@@ -926,9 +924,9 @@
"rules_browsers_firefox_win64": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
"sha256": "36ff9e150875aa48a0af9eec3eb67f66dddd8efac5c743265371a72ae3e796c4",
"sha256": "216870c89648f32450cfefb5cec417fcd66d480d5dc83f894bf99f5fd7f38dbb",
"urls": [
"https://archive.mozilla.org/pub/firefox/releases/147.0/win64/en-US/Firefox%20Setup%20147.0.exe"
"https://archive.mozilla.org/pub/firefox/releases/146.0/win64/en-US/Firefox%20Setup%20146.0.exe"
],
"named_files": {
"FIREFOX": "core/firefox.exe"
@@ -945,7 +943,7 @@
},
"@@rules_fuzzing+//fuzzing/private:extensions.bzl%non_module_dependencies": {
"general": {
"bzlTransitiveDigest": "4LouzhF/yT117s7peGnNs9ROomiJXC6Zl5R0oI21jho=",
"bzlTransitiveDigest": "WHRlQQnxW7e7XMRBhq7SARkDarLDOAbg6iLaJpk5QYM=",
"usagesDigest": "wy6ISK6UOcBEjj/mvJ/S3WeXoO67X+1llb9yPyFtPgc=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
@@ -1028,7 +1026,7 @@
},
"@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": {
"general": {
"bzlTransitiveDigest": "nvW/NrBXlAmiQw99EMGKkLaD2KbNp2mQDlxdfpr+0Ls=",
"bzlTransitiveDigest": "rL/34P1aFDq2GqVC2zCFgQ8nTuOC6ziogocpvG50Qz8=",
"usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
@@ -1093,7 +1091,7 @@
"@@rules_nodejs+//nodejs:extensions.bzl%node": {
"general": {
"bzlTransitiveDigest": "4pUxCNc22K4I+6+4Nxu52Hur12tFRfa1JMsN5mdDv60=",
"usagesDigest": "pjXeuItYQffkhMcQv+aU/G0SB0QAmyOC9W7iq63eW08=",
"usagesDigest": "yiylduJft13ULV5TUSGMYtVEKVnH/fJkG3SYNNrzFsQ=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
@@ -1103,46 +1101,46 @@
"attributes": {
"node_download_auth": {},
"node_repositories": {
"22.22.1-darwin_arm64": [
"node-v22.22.1-darwin-arm64.tar.gz",
"node-v22.22.1-darwin-arm64",
"679ad4966339e4ef4900f57996714864e4211b898825bb840c3086c419fbcef2"
"22.22.0-darwin_arm64": [
"node-v22.22.0-darwin-arm64.tar.gz",
"node-v22.22.0-darwin-arm64",
"5ed4db0fcf1eaf84d91ad12462631d73bf4576c1377e192d222e48026a902640"
],
"22.22.1-darwin_amd64": [
"node-v22.22.1-darwin-x64.tar.gz",
"node-v22.22.1-darwin-x64",
"07b13722d558790fca20bb1ecf61bde24b7a4863111f7be77fc57251a407359a"
"22.22.0-darwin_amd64": [
"node-v22.22.0-darwin-x64.tar.gz",
"node-v22.22.0-darwin-x64",
"5ea50c9d6dea3dfa3abb66b2656f7a4e1c8cef23432b558d45fb538c7b5dedce"
],
"22.22.1-linux_arm64": [
"node-v22.22.1-linux-arm64.tar.xz",
"node-v22.22.1-linux-arm64",
"0f3550d58d45e5d3cf7103d9e3f69937f09fe82fb5dd474c66a5d816fa58c9ee"
"22.22.0-linux_arm64": [
"node-v22.22.0-linux-arm64.tar.xz",
"node-v22.22.0-linux-arm64",
"1bf1eb9ee63ffc4e5d324c0b9b62cf4a289f44332dfef9607cea1a0d9596ba6f"
],
"22.22.1-linux_ppc64le": [
"node-v22.22.1-linux-ppc64le.tar.xz",
"node-v22.22.1-linux-ppc64le",
"a65a44cf0224505f052b90357b763dbc1ea9148f4f5f2284f0596cf2000f819b"
"22.22.0-linux_ppc64le": [
"node-v22.22.0-linux-ppc64le.tar.xz",
"node-v22.22.0-linux-ppc64le",
"d83b9957431cc18e1fc143a4b99f89cde7b8a18f53ef392231b4336afd058865"
],
"22.22.1-linux_s390x": [
"node-v22.22.1-linux-s390x.tar.xz",
"node-v22.22.1-linux-s390x",
"e08b3a73d0bd840e008f589e4be4a2ef3d4a0c59015f4f20a04ed7fc968042a2"
"22.22.0-linux_s390x": [
"node-v22.22.0-linux-s390x.tar.xz",
"node-v22.22.0-linux-s390x",
"5aa0e520689448c4233e8d73f284e8e0634fdcd32b479735698494be5641f3e4"
],
"22.22.1-linux_amd64": [
"node-v22.22.1-linux-x64.tar.xz",
"node-v22.22.1-linux-x64",
"9a6bc82f9b491279147219f6a18add1e18424dce90d41d2a5fcd69d4924ba3aa"
"22.22.0-linux_amd64": [
"node-v22.22.0-linux-x64.tar.xz",
"node-v22.22.0-linux-x64",
"9aa8e9d2298ab68c600bd6fb86a6c13bce11a4eca1ba9b39d79fa021755d7c37"
],
"22.22.1-windows_amd64": [
"node-v22.22.1-win-x64.zip",
"node-v22.22.1-win-x64",
"877cb93829e14fffbbc7903e7d8037336c9a79f3ea43c5d0b8c2379b79da56de"
"22.22.0-windows_amd64": [
"node-v22.22.0-win-x64.zip",
"node-v22.22.0-win-x64",
"c97fa376d2becdc8863fcd3ca2dd9a83a9f3468ee7ccf7a6d076ec66a645c77a"
]
},
"node_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
"node_version": "22.22.1",
"node_version": "22.22.0",
"include_headers": false,
"platform": "linux_amd64"
}
@@ -1152,46 +1150,46 @@
"attributes": {
"node_download_auth": {},
"node_repositories": {
"22.22.1-darwin_arm64": [
"node-v22.22.1-darwin-arm64.tar.gz",
"node-v22.22.1-darwin-arm64",
"679ad4966339e4ef4900f57996714864e4211b898825bb840c3086c419fbcef2"
"22.22.0-darwin_arm64": [
"node-v22.22.0-darwin-arm64.tar.gz",
"node-v22.22.0-darwin-arm64",
"5ed4db0fcf1eaf84d91ad12462631d73bf4576c1377e192d222e48026a902640"
],
"22.22.1-darwin_amd64": [
"node-v22.22.1-darwin-x64.tar.gz",
"node-v22.22.1-darwin-x64",
"07b13722d558790fca20bb1ecf61bde24b7a4863111f7be77fc57251a407359a"
"22.22.0-darwin_amd64": [
"node-v22.22.0-darwin-x64.tar.gz",
"node-v22.22.0-darwin-x64",
"5ea50c9d6dea3dfa3abb66b2656f7a4e1c8cef23432b558d45fb538c7b5dedce"
],
"22.22.1-linux_arm64": [
"node-v22.22.1-linux-arm64.tar.xz",
"node-v22.22.1-linux-arm64",
"0f3550d58d45e5d3cf7103d9e3f69937f09fe82fb5dd474c66a5d816fa58c9ee"
"22.22.0-linux_arm64": [
"node-v22.22.0-linux-arm64.tar.xz",
"node-v22.22.0-linux-arm64",
"1bf1eb9ee63ffc4e5d324c0b9b62cf4a289f44332dfef9607cea1a0d9596ba6f"
],
"22.22.1-linux_ppc64le": [
"node-v22.22.1-linux-ppc64le.tar.xz",
"node-v22.22.1-linux-ppc64le",
"a65a44cf0224505f052b90357b763dbc1ea9148f4f5f2284f0596cf2000f819b"
"22.22.0-linux_ppc64le": [
"node-v22.22.0-linux-ppc64le.tar.xz",
"node-v22.22.0-linux-ppc64le",
"d83b9957431cc18e1fc143a4b99f89cde7b8a18f53ef392231b4336afd058865"
],
"22.22.1-linux_s390x": [
"node-v22.22.1-linux-s390x.tar.xz",
"node-v22.22.1-linux-s390x",
"e08b3a73d0bd840e008f589e4be4a2ef3d4a0c59015f4f20a04ed7fc968042a2"
"22.22.0-linux_s390x": [
"node-v22.22.0-linux-s390x.tar.xz",
"node-v22.22.0-linux-s390x",
"5aa0e520689448c4233e8d73f284e8e0634fdcd32b479735698494be5641f3e4"
],
"22.22.1-linux_amd64": [
"node-v22.22.1-linux-x64.tar.xz",
"node-v22.22.1-linux-x64",
"9a6bc82f9b491279147219f6a18add1e18424dce90d41d2a5fcd69d4924ba3aa"
"22.22.0-linux_amd64": [
"node-v22.22.0-linux-x64.tar.xz",
"node-v22.22.0-linux-x64",
"9aa8e9d2298ab68c600bd6fb86a6c13bce11a4eca1ba9b39d79fa021755d7c37"
],
"22.22.1-windows_amd64": [
"node-v22.22.1-win-x64.zip",
"node-v22.22.1-win-x64",
"877cb93829e14fffbbc7903e7d8037336c9a79f3ea43c5d0b8c2379b79da56de"
"22.22.0-windows_amd64": [
"node-v22.22.0-win-x64.zip",
"node-v22.22.0-win-x64",
"c97fa376d2becdc8863fcd3ca2dd9a83a9f3468ee7ccf7a6d076ec66a645c77a"
]
},
"node_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
"node_version": "22.22.1",
"node_version": "22.22.0",
"include_headers": false,
"platform": "linux_arm64"
}
@@ -1201,46 +1199,46 @@
"attributes": {
"node_download_auth": {},
"node_repositories": {
"22.22.1-darwin_arm64": [
"node-v22.22.1-darwin-arm64.tar.gz",
"node-v22.22.1-darwin-arm64",
"679ad4966339e4ef4900f57996714864e4211b898825bb840c3086c419fbcef2"
"22.22.0-darwin_arm64": [
"node-v22.22.0-darwin-arm64.tar.gz",
"node-v22.22.0-darwin-arm64",
"5ed4db0fcf1eaf84d91ad12462631d73bf4576c1377e192d222e48026a902640"
],
"22.22.1-darwin_amd64": [
"node-v22.22.1-darwin-x64.tar.gz",
"node-v22.22.1-darwin-x64",
"07b13722d558790fca20bb1ecf61bde24b7a4863111f7be77fc57251a407359a"
"22.22.0-darwin_amd64": [
"node-v22.22.0-darwin-x64.tar.gz",
"node-v22.22.0-darwin-x64",
"5ea50c9d6dea3dfa3abb66b2656f7a4e1c8cef23432b558d45fb538c7b5dedce"
],
"22.22.1-linux_arm64": [
"node-v22.22.1-linux-arm64.tar.xz",
"node-v22.22.1-linux-arm64",
"0f3550d58d45e5d3cf7103d9e3f69937f09fe82fb5dd474c66a5d816fa58c9ee"
"22.22.0-linux_arm64": [
"node-v22.22.0-linux-arm64.tar.xz",
"node-v22.22.0-linux-arm64",
"1bf1eb9ee63ffc4e5d324c0b9b62cf4a289f44332dfef9607cea1a0d9596ba6f"
],
"22.22.1-linux_ppc64le": [
"node-v22.22.1-linux-ppc64le.tar.xz",
"node-v22.22.1-linux-ppc64le",
"a65a44cf0224505f052b90357b763dbc1ea9148f4f5f2284f0596cf2000f819b"
"22.22.0-linux_ppc64le": [
"node-v22.22.0-linux-ppc64le.tar.xz",
"node-v22.22.0-linux-ppc64le",
"d83b9957431cc18e1fc143a4b99f89cde7b8a18f53ef392231b4336afd058865"
],
"22.22.1-linux_s390x": [
"node-v22.22.1-linux-s390x.tar.xz",
"node-v22.22.1-linux-s390x",
"e08b3a73d0bd840e008f589e4be4a2ef3d4a0c59015f4f20a04ed7fc968042a2"
"22.22.0-linux_s390x": [
"node-v22.22.0-linux-s390x.tar.xz",
"node-v22.22.0-linux-s390x",
"5aa0e520689448c4233e8d73f284e8e0634fdcd32b479735698494be5641f3e4"
],
"22.22.1-linux_amd64": [
"node-v22.22.1-linux-x64.tar.xz",
"node-v22.22.1-linux-x64",
"9a6bc82f9b491279147219f6a18add1e18424dce90d41d2a5fcd69d4924ba3aa"
"22.22.0-linux_amd64": [
"node-v22.22.0-linux-x64.tar.xz",
"node-v22.22.0-linux-x64",
"9aa8e9d2298ab68c600bd6fb86a6c13bce11a4eca1ba9b39d79fa021755d7c37"
],
"22.22.1-windows_amd64": [
"node-v22.22.1-win-x64.zip",
"node-v22.22.1-win-x64",
"877cb93829e14fffbbc7903e7d8037336c9a79f3ea43c5d0b8c2379b79da56de"
"22.22.0-windows_amd64": [
"node-v22.22.0-win-x64.zip",
"node-v22.22.0-win-x64",
"c97fa376d2becdc8863fcd3ca2dd9a83a9f3468ee7ccf7a6d076ec66a645c77a"
]
},
"node_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
"node_version": "22.22.1",
"node_version": "22.22.0",
"include_headers": false,
"platform": "linux_s390x"
}
@@ -1250,46 +1248,46 @@
"attributes": {
"node_download_auth": {},
"node_repositories": {
"22.22.1-darwin_arm64": [
"node-v22.22.1-darwin-arm64.tar.gz",
"node-v22.22.1-darwin-arm64",
"679ad4966339e4ef4900f57996714864e4211b898825bb840c3086c419fbcef2"
"22.22.0-darwin_arm64": [
"node-v22.22.0-darwin-arm64.tar.gz",
"node-v22.22.0-darwin-arm64",
"5ed4db0fcf1eaf84d91ad12462631d73bf4576c1377e192d222e48026a902640"
],
"22.22.1-darwin_amd64": [
"node-v22.22.1-darwin-x64.tar.gz",
"node-v22.22.1-darwin-x64",
"07b13722d558790fca20bb1ecf61bde24b7a4863111f7be77fc57251a407359a"
"22.22.0-darwin_amd64": [
"node-v22.22.0-darwin-x64.tar.gz",
"node-v22.22.0-darwin-x64",
"5ea50c9d6dea3dfa3abb66b2656f7a4e1c8cef23432b558d45fb538c7b5dedce"
],
"22.22.1-linux_arm64": [
"node-v22.22.1-linux-arm64.tar.xz",
"node-v22.22.1-linux-arm64",
"0f3550d58d45e5d3cf7103d9e3f69937f09fe82fb5dd474c66a5d816fa58c9ee"
"22.22.0-linux_arm64": [
"node-v22.22.0-linux-arm64.tar.xz",
"node-v22.22.0-linux-arm64",
"1bf1eb9ee63ffc4e5d324c0b9b62cf4a289f44332dfef9607cea1a0d9596ba6f"
],
"22.22.1-linux_ppc64le": [
"node-v22.22.1-linux-ppc64le.tar.xz",
"node-v22.22.1-linux-ppc64le",
"a65a44cf0224505f052b90357b763dbc1ea9148f4f5f2284f0596cf2000f819b"
"22.22.0-linux_ppc64le": [
"node-v22.22.0-linux-ppc64le.tar.xz",
"node-v22.22.0-linux-ppc64le",
"d83b9957431cc18e1fc143a4b99f89cde7b8a18f53ef392231b4336afd058865"
],
"22.22.1-linux_s390x": [
"node-v22.22.1-linux-s390x.tar.xz",
"node-v22.22.1-linux-s390x",
"e08b3a73d0bd840e008f589e4be4a2ef3d4a0c59015f4f20a04ed7fc968042a2"
"22.22.0-linux_s390x": [
"node-v22.22.0-linux-s390x.tar.xz",
"node-v22.22.0-linux-s390x",
"5aa0e520689448c4233e8d73f284e8e0634fdcd32b479735698494be5641f3e4"
],
"22.22.1-linux_amd64": [
"node-v22.22.1-linux-x64.tar.xz",
"node-v22.22.1-linux-x64",
"9a6bc82f9b491279147219f6a18add1e18424dce90d41d2a5fcd69d4924ba3aa"
"22.22.0-linux_amd64": [
"node-v22.22.0-linux-x64.tar.xz",
"node-v22.22.0-linux-x64",
"9aa8e9d2298ab68c600bd6fb86a6c13bce11a4eca1ba9b39d79fa021755d7c37"
],
"22.22.1-windows_amd64": [
"node-v22.22.1-win-x64.zip",
"node-v22.22.1-win-x64",
"877cb93829e14fffbbc7903e7d8037336c9a79f3ea43c5d0b8c2379b79da56de"
"22.22.0-windows_amd64": [
"node-v22.22.0-win-x64.zip",
"node-v22.22.0-win-x64",
"c97fa376d2becdc8863fcd3ca2dd9a83a9f3468ee7ccf7a6d076ec66a645c77a"
]
},
"node_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
"node_version": "22.22.1",
"node_version": "22.22.0",
"include_headers": false,
"platform": "linux_ppc64le"
}
@@ -1299,46 +1297,46 @@
"attributes": {
"node_download_auth": {},
"node_repositories": {
"22.22.1-darwin_arm64": [
"node-v22.22.1-darwin-arm64.tar.gz",
"node-v22.22.1-darwin-arm64",
"679ad4966339e4ef4900f57996714864e4211b898825bb840c3086c419fbcef2"
"22.22.0-darwin_arm64": [
"node-v22.22.0-darwin-arm64.tar.gz",
"node-v22.22.0-darwin-arm64",
"5ed4db0fcf1eaf84d91ad12462631d73bf4576c1377e192d222e48026a902640"
],
"22.22.1-darwin_amd64": [
"node-v22.22.1-darwin-x64.tar.gz",
"node-v22.22.1-darwin-x64",
"07b13722d558790fca20bb1ecf61bde24b7a4863111f7be77fc57251a407359a"
"22.22.0-darwin_amd64": [
"node-v22.22.0-darwin-x64.tar.gz",
"node-v22.22.0-darwin-x64",
"5ea50c9d6dea3dfa3abb66b2656f7a4e1c8cef23432b558d45fb538c7b5dedce"
],
"22.22.1-linux_arm64": [
"node-v22.22.1-linux-arm64.tar.xz",
"node-v22.22.1-linux-arm64",
"0f3550d58d45e5d3cf7103d9e3f69937f09fe82fb5dd474c66a5d816fa58c9ee"
"22.22.0-linux_arm64": [
"node-v22.22.0-linux-arm64.tar.xz",
"node-v22.22.0-linux-arm64",
"1bf1eb9ee63ffc4e5d324c0b9b62cf4a289f44332dfef9607cea1a0d9596ba6f"
],
"22.22.1-linux_ppc64le": [
"node-v22.22.1-linux-ppc64le.tar.xz",
"node-v22.22.1-linux-ppc64le",
"a65a44cf0224505f052b90357b763dbc1ea9148f4f5f2284f0596cf2000f819b"
"22.22.0-linux_ppc64le": [
"node-v22.22.0-linux-ppc64le.tar.xz",
"node-v22.22.0-linux-ppc64le",
"d83b9957431cc18e1fc143a4b99f89cde7b8a18f53ef392231b4336afd058865"
],
"22.22.1-linux_s390x": [
"node-v22.22.1-linux-s390x.tar.xz",
"node-v22.22.1-linux-s390x",
"e08b3a73d0bd840e008f589e4be4a2ef3d4a0c59015f4f20a04ed7fc968042a2"
"22.22.0-linux_s390x": [
"node-v22.22.0-linux-s390x.tar.xz",
"node-v22.22.0-linux-s390x",
"5aa0e520689448c4233e8d73f284e8e0634fdcd32b479735698494be5641f3e4"
],
"22.22.1-linux_amd64": [
"node-v22.22.1-linux-x64.tar.xz",
"node-v22.22.1-linux-x64",
"9a6bc82f9b491279147219f6a18add1e18424dce90d41d2a5fcd69d4924ba3aa"
"22.22.0-linux_amd64": [
"node-v22.22.0-linux-x64.tar.xz",
"node-v22.22.0-linux-x64",
"9aa8e9d2298ab68c600bd6fb86a6c13bce11a4eca1ba9b39d79fa021755d7c37"
],
"22.22.1-windows_amd64": [
"node-v22.22.1-win-x64.zip",
"node-v22.22.1-win-x64",
"877cb93829e14fffbbc7903e7d8037336c9a79f3ea43c5d0b8c2379b79da56de"
"22.22.0-windows_amd64": [
"node-v22.22.0-win-x64.zip",
"node-v22.22.0-win-x64",
"c97fa376d2becdc8863fcd3ca2dd9a83a9f3468ee7ccf7a6d076ec66a645c77a"
]
},
"node_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
"node_version": "22.22.1",
"node_version": "22.22.0",
"include_headers": false,
"platform": "darwin_amd64"
}
@@ -1348,46 +1346,46 @@
"attributes": {
"node_download_auth": {},
"node_repositories": {
"22.22.1-darwin_arm64": [
"node-v22.22.1-darwin-arm64.tar.gz",
"node-v22.22.1-darwin-arm64",
"679ad4966339e4ef4900f57996714864e4211b898825bb840c3086c419fbcef2"
"22.22.0-darwin_arm64": [
"node-v22.22.0-darwin-arm64.tar.gz",
"node-v22.22.0-darwin-arm64",
"5ed4db0fcf1eaf84d91ad12462631d73bf4576c1377e192d222e48026a902640"
],
"22.22.1-darwin_amd64": [
"node-v22.22.1-darwin-x64.tar.gz",
"node-v22.22.1-darwin-x64",
"07b13722d558790fca20bb1ecf61bde24b7a4863111f7be77fc57251a407359a"
"22.22.0-darwin_amd64": [
"node-v22.22.0-darwin-x64.tar.gz",
"node-v22.22.0-darwin-x64",
"5ea50c9d6dea3dfa3abb66b2656f7a4e1c8cef23432b558d45fb538c7b5dedce"
],
"22.22.1-linux_arm64": [
"node-v22.22.1-linux-arm64.tar.xz",
"node-v22.22.1-linux-arm64",
"0f3550d58d45e5d3cf7103d9e3f69937f09fe82fb5dd474c66a5d816fa58c9ee"
"22.22.0-linux_arm64": [
"node-v22.22.0-linux-arm64.tar.xz",
"node-v22.22.0-linux-arm64",
"1bf1eb9ee63ffc4e5d324c0b9b62cf4a289f44332dfef9607cea1a0d9596ba6f"
],
"22.22.1-linux_ppc64le": [
"node-v22.22.1-linux-ppc64le.tar.xz",
"node-v22.22.1-linux-ppc64le",
"a65a44cf0224505f052b90357b763dbc1ea9148f4f5f2284f0596cf2000f819b"
"22.22.0-linux_ppc64le": [
"node-v22.22.0-linux-ppc64le.tar.xz",
"node-v22.22.0-linux-ppc64le",
"d83b9957431cc18e1fc143a4b99f89cde7b8a18f53ef392231b4336afd058865"
],
"22.22.1-linux_s390x": [
"node-v22.22.1-linux-s390x.tar.xz",
"node-v22.22.1-linux-s390x",
"e08b3a73d0bd840e008f589e4be4a2ef3d4a0c59015f4f20a04ed7fc968042a2"
"22.22.0-linux_s390x": [
"node-v22.22.0-linux-s390x.tar.xz",
"node-v22.22.0-linux-s390x",
"5aa0e520689448c4233e8d73f284e8e0634fdcd32b479735698494be5641f3e4"
],
"22.22.1-linux_amd64": [
"node-v22.22.1-linux-x64.tar.xz",
"node-v22.22.1-linux-x64",
"9a6bc82f9b491279147219f6a18add1e18424dce90d41d2a5fcd69d4924ba3aa"
"22.22.0-linux_amd64": [
"node-v22.22.0-linux-x64.tar.xz",
"node-v22.22.0-linux-x64",
"9aa8e9d2298ab68c600bd6fb86a6c13bce11a4eca1ba9b39d79fa021755d7c37"
],
"22.22.1-windows_amd64": [
"node-v22.22.1-win-x64.zip",
"node-v22.22.1-win-x64",
"877cb93829e14fffbbc7903e7d8037336c9a79f3ea43c5d0b8c2379b79da56de"
"22.22.0-windows_amd64": [
"node-v22.22.0-win-x64.zip",
"node-v22.22.0-win-x64",
"c97fa376d2becdc8863fcd3ca2dd9a83a9f3468ee7ccf7a6d076ec66a645c77a"
]
},
"node_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
"node_version": "22.22.1",
"node_version": "22.22.0",
"include_headers": false,
"platform": "darwin_arm64"
}
@@ -1397,46 +1395,46 @@
"attributes": {
"node_download_auth": {},
"node_repositories": {
"22.22.1-darwin_arm64": [
"node-v22.22.1-darwin-arm64.tar.gz",
"node-v22.22.1-darwin-arm64",
"679ad4966339e4ef4900f57996714864e4211b898825bb840c3086c419fbcef2"
"22.22.0-darwin_arm64": [
"node-v22.22.0-darwin-arm64.tar.gz",
"node-v22.22.0-darwin-arm64",
"5ed4db0fcf1eaf84d91ad12462631d73bf4576c1377e192d222e48026a902640"
],
"22.22.1-darwin_amd64": [
"node-v22.22.1-darwin-x64.tar.gz",
"node-v22.22.1-darwin-x64",
"07b13722d558790fca20bb1ecf61bde24b7a4863111f7be77fc57251a407359a"
"22.22.0-darwin_amd64": [
"node-v22.22.0-darwin-x64.tar.gz",
"node-v22.22.0-darwin-x64",
"5ea50c9d6dea3dfa3abb66b2656f7a4e1c8cef23432b558d45fb538c7b5dedce"
],
"22.22.1-linux_arm64": [
"node-v22.22.1-linux-arm64.tar.xz",
"node-v22.22.1-linux-arm64",
"0f3550d58d45e5d3cf7103d9e3f69937f09fe82fb5dd474c66a5d816fa58c9ee"
"22.22.0-linux_arm64": [
"node-v22.22.0-linux-arm64.tar.xz",
"node-v22.22.0-linux-arm64",
"1bf1eb9ee63ffc4e5d324c0b9b62cf4a289f44332dfef9607cea1a0d9596ba6f"
],
"22.22.1-linux_ppc64le": [
"node-v22.22.1-linux-ppc64le.tar.xz",
"node-v22.22.1-linux-ppc64le",
"a65a44cf0224505f052b90357b763dbc1ea9148f4f5f2284f0596cf2000f819b"
"22.22.0-linux_ppc64le": [
"node-v22.22.0-linux-ppc64le.tar.xz",
"node-v22.22.0-linux-ppc64le",
"d83b9957431cc18e1fc143a4b99f89cde7b8a18f53ef392231b4336afd058865"
],
"22.22.1-linux_s390x": [
"node-v22.22.1-linux-s390x.tar.xz",
"node-v22.22.1-linux-s390x",
"e08b3a73d0bd840e008f589e4be4a2ef3d4a0c59015f4f20a04ed7fc968042a2"
"22.22.0-linux_s390x": [
"node-v22.22.0-linux-s390x.tar.xz",
"node-v22.22.0-linux-s390x",
"5aa0e520689448c4233e8d73f284e8e0634fdcd32b479735698494be5641f3e4"
],
"22.22.1-linux_amd64": [
"node-v22.22.1-linux-x64.tar.xz",
"node-v22.22.1-linux-x64",
"9a6bc82f9b491279147219f6a18add1e18424dce90d41d2a5fcd69d4924ba3aa"
"22.22.0-linux_amd64": [
"node-v22.22.0-linux-x64.tar.xz",
"node-v22.22.0-linux-x64",
"9aa8e9d2298ab68c600bd6fb86a6c13bce11a4eca1ba9b39d79fa021755d7c37"
],
"22.22.1-windows_amd64": [
"node-v22.22.1-win-x64.zip",
"node-v22.22.1-win-x64",
"877cb93829e14fffbbc7903e7d8037336c9a79f3ea43c5d0b8c2379b79da56de"
"22.22.0-windows_amd64": [
"node-v22.22.0-win-x64.zip",
"node-v22.22.0-win-x64",
"c97fa376d2becdc8863fcd3ca2dd9a83a9f3468ee7ccf7a6d076ec66a645c77a"
]
},
"node_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
"node_version": "22.22.1",
"node_version": "22.22.0",
"include_headers": false,
"platform": "windows_amd64"
}
@@ -1446,46 +1444,46 @@
"attributes": {
"node_download_auth": {},
"node_repositories": {
"22.22.1-darwin_arm64": [
"node-v22.22.1-darwin-arm64.tar.gz",
"node-v22.22.1-darwin-arm64",
"679ad4966339e4ef4900f57996714864e4211b898825bb840c3086c419fbcef2"
"22.22.0-darwin_arm64": [
"node-v22.22.0-darwin-arm64.tar.gz",
"node-v22.22.0-darwin-arm64",
"5ed4db0fcf1eaf84d91ad12462631d73bf4576c1377e192d222e48026a902640"
],
"22.22.1-darwin_amd64": [
"node-v22.22.1-darwin-x64.tar.gz",
"node-v22.22.1-darwin-x64",
"07b13722d558790fca20bb1ecf61bde24b7a4863111f7be77fc57251a407359a"
"22.22.0-darwin_amd64": [
"node-v22.22.0-darwin-x64.tar.gz",
"node-v22.22.0-darwin-x64",
"5ea50c9d6dea3dfa3abb66b2656f7a4e1c8cef23432b558d45fb538c7b5dedce"
],
"22.22.1-linux_arm64": [
"node-v22.22.1-linux-arm64.tar.xz",
"node-v22.22.1-linux-arm64",
"0f3550d58d45e5d3cf7103d9e3f69937f09fe82fb5dd474c66a5d816fa58c9ee"
"22.22.0-linux_arm64": [
"node-v22.22.0-linux-arm64.tar.xz",
"node-v22.22.0-linux-arm64",
"1bf1eb9ee63ffc4e5d324c0b9b62cf4a289f44332dfef9607cea1a0d9596ba6f"
],
"22.22.1-linux_ppc64le": [
"node-v22.22.1-linux-ppc64le.tar.xz",
"node-v22.22.1-linux-ppc64le",
"a65a44cf0224505f052b90357b763dbc1ea9148f4f5f2284f0596cf2000f819b"
"22.22.0-linux_ppc64le": [
"node-v22.22.0-linux-ppc64le.tar.xz",
"node-v22.22.0-linux-ppc64le",
"d83b9957431cc18e1fc143a4b99f89cde7b8a18f53ef392231b4336afd058865"
],
"22.22.1-linux_s390x": [
"node-v22.22.1-linux-s390x.tar.xz",
"node-v22.22.1-linux-s390x",
"e08b3a73d0bd840e008f589e4be4a2ef3d4a0c59015f4f20a04ed7fc968042a2"
"22.22.0-linux_s390x": [
"node-v22.22.0-linux-s390x.tar.xz",
"node-v22.22.0-linux-s390x",
"5aa0e520689448c4233e8d73f284e8e0634fdcd32b479735698494be5641f3e4"
],
"22.22.1-linux_amd64": [
"node-v22.22.1-linux-x64.tar.xz",
"node-v22.22.1-linux-x64",
"9a6bc82f9b491279147219f6a18add1e18424dce90d41d2a5fcd69d4924ba3aa"
"22.22.0-linux_amd64": [
"node-v22.22.0-linux-x64.tar.xz",
"node-v22.22.0-linux-x64",
"9aa8e9d2298ab68c600bd6fb86a6c13bce11a4eca1ba9b39d79fa021755d7c37"
],
"22.22.1-windows_amd64": [
"node-v22.22.1-win-x64.zip",
"node-v22.22.1-win-x64",
"877cb93829e14fffbbc7903e7d8037336c9a79f3ea43c5d0b8c2379b79da56de"
"22.22.0-windows_amd64": [
"node-v22.22.0-win-x64.zip",
"node-v22.22.0-win-x64",
"c97fa376d2becdc8863fcd3ca2dd9a83a9f3468ee7ccf7a6d076ec66a645c77a"
]
},
"node_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
"node_version": "22.22.1",
"node_version": "22.22.0",
"include_headers": false,
"platform": "windows_arm64"
}
@@ -1514,7 +1512,7 @@
},
"@@rules_python+//python/extensions:pip.bzl%pip": {
"general": {
"bzlTransitiveDigest": "LFkevJ0u48WgTVCyU07LkRD8iYwsRUs2M7uSFHkQAW0=",
"bzlTransitiveDigest": "d3ENjFH8qMwmOrkcb3c9JYqQ5hJ6owjfbSr24KY0Ugg=",
"usagesDigest": "/9NP3RV6/DWuNdYAsIU/8UCgCX0TdPUJr0X6O+0lrtk=",
"recordedFileInputs": {
"@@protobuf+//python/requirements.txt": "983be60d3cec4b319dcab6d48aeb3f5b2f7c3350f26b3a9e97486c37967c73c5",
@@ -4308,8 +4306,8 @@
},
"@@yq.bzl+//yq:extensions.bzl%yq": {
"general": {
"bzlTransitiveDigest": "UfFMy8CWK4/dVo/tfaSAIYUiDGNAPes5eRllx9O9Q9Q=",
"usagesDigest": "LPHUB1lzZAsPKLeNh6HnzS3+5eXDr8A5CKmoD+VBOA4=",
"bzlTransitiveDigest": "tDqk+ntWTdxNAWPDjRY1uITgHbti2jcXR5ZdinltBs0=",
"usagesDigest": "soFLPyR23RWNxgPKSnibWfWF51i2rx0N0Vs4WyvVNpE=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
-25
View File
@@ -60,7 +60,6 @@ APPLICATION_DEPS = [
":node_modules/@lezer/highlight",
":node_modules/@lezer/javascript",
":node_modules/@stackblitz/sdk",
":node_modules/shiki",
":node_modules/@typescript/vfs",
":node_modules/@webcontainer/api",
":node_modules/@xterm/addon-fit",
@@ -92,30 +91,6 @@ APPLICATION_DEPS = [
":node_modules/crelt",
":node_modules/style-mod",
":node_modules/w3c-keyname",
#
":node_modules/@shikijs/themes",
":node_modules/@shikijs/langs",
":node_modules/@shikijs/engine-oniguruma",
":node_modules/@shikijs/core",
":node_modules/@shikijs/engine-javascript",
":node_modules/@shikijs/types",
":node_modules/@shikijs/vscode-textmate",
":node_modules/hast-util-to-html",
# ":node_modules/oniguruma-to-es",
":node_modules/ccount",
":node_modules/comma-separated-tokens",
":node_modules/hast-util-whitespace",
":node_modules/html-void-elements",
# ":node_modules/oniguruma-parser",
":node_modules/property-information",
# ":node_modules/regex-recursion",
# ":node_modules/regex",
":node_modules/space-separated-tokens",
":node_modules/stringify-entities",
":node_modules/zwitch",
":node_modules/character-entities-html4",
":node_modules/character-entities-legacy",
# ":node_modules/regex-utilities",
]
TEST_FILES = APPLICATION_FILES + glob(["src/app/**/*.spec.ts"])
+30 -48
View File
@@ -1,25 +1,25 @@
{
"dependencies": {
"@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",
"@algolia/client-common": "5.46.2",
"@algolia/client-search": "5.46.2",
"@algolia/requester-browser-xhr": "5.46.2",
"@algolia/requester-node-http": "5.46.2",
"@angular/animations": "workspace:*",
"@angular/aria": "21.2.0",
"@angular/build": "21.2.0",
"@angular/cdk": "21.2.0",
"@angular/cli": "21.2.0",
"@angular/aria": "21.1.3",
"@angular/build": "21.1.2",
"@angular/cdk": "21.1.3",
"@angular/cli": "21.1.2",
"@angular/common": "workspace:*",
"@angular/compiler": "workspace:*",
"@angular/compiler-cli": "workspace:*",
"@angular/core": "workspace:*",
"@angular/docs": "workspace:*",
"@angular/forms": "workspace:*",
"@angular/material": "21.2.0",
"@angular/material": "21.1.3",
"@angular/platform-browser": "workspace:*",
"@angular/platform-server": "workspace:*",
"@angular/router": "workspace:*",
"@angular/ssr": "21.2.0",
"@angular/ssr": "21.1.2",
"@codemirror/autocomplete": "6.20.0",
"@codemirror/commands": "6.10.1",
"@codemirror/lang-angular": "0.1.4",
@@ -28,75 +28,57 @@
"@codemirror/lang-javascript": "6.2.4",
"@codemirror/lang-sass": "6.0.2",
"@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",
"@codemirror/lint": "6.9.2",
"@codemirror/search": "6.5.11",
"@codemirror/state": "6.5.3",
"@codemirror/view": "6.39.9",
"@lezer/common": "1.5.0",
"@lezer/css": "1.3.0",
"@lezer/highlight": "1.2.3",
"@lezer/html": "1.3.13",
"@lezer/javascript": "1.5.4",
"@lezer/lr": "1.4.8",
"@lezer/lr": "1.4.7",
"@lezer/sass": "1.1.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.0",
"@types/dom-navigation": "1.0.6",
"@types/jasmine": "6.0.0",
"@types/jasmine": "5.1.13",
"@types/jsdom": "27.0.0",
"@types/node": "24.10.11",
"@types/node": "24.10.4",
"@typescript/vfs": "1.6.2",
"@webcontainer/api": "1.6.1",
"@xterm/addon-fit": "0.11.0",
"@xterm/xterm": "6.0.0",
"algoliasearch": "5.48.0",
"algoliasearch": "5.46.2",
"angular-split": "20.0.0",
"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.6",
"diff": "8.0.3",
"diff": "8.0.2",
"emoji-regex": "10.6.0",
"fflate": "0.8.2",
"hast-util-to-html": "^9.0.5",
"hast-util-whitespace": "^3.0.0",
"html-void-elements": "^3.0.0",
"jsdom": "28.0.0",
"jasmine-core": "6.1.0",
"jasmine-core": "5.13.0",
"jsdom": "27.4.0",
"karma-chrome-launcher": "3.2.0",
"karma-coverage": "2.2.1",
"karma-jasmine": "5.1.0",
"karma-jasmine-html-reporter": "2.2.0",
"karma-jasmine-html-reporter": "2.1.0",
"marked": "17.0.1",
"mermaid": "11.12.2",
"ngx-progressbar": "14.0.0",
"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",
"playwright-core": "1.57.0",
"preact": "10.28.2",
"preact-render-to-string": "6.6.5",
"prettier": "3.8.0",
"rxjs": "7.8.2",
"shiki": "3.22.0",
"space-separated-tokens": "^2.0.2",
"stringify-entities": "^4.0.4",
"shiki": "3.21.0",
"style-mod": "4.1.3",
"tinyglobby": "0.2.15",
"tslib": "2.8.1",
"typescript": "5.9.3",
"w3c-keyname": "2.2.8",
"zwitch": "^2.0.4"
"w3c-keyname": "2.2.8"
},
"devDependencies": {
"autoprefixer": "10.4.24",
"autoprefixer": "10.4.23",
"karma": "~6.4.4",
"postcss": "8.5.6",
"tailwindcss": "3.4.19"
+1 -3
View File
@@ -19,8 +19,7 @@ ts_project(
tsconfig = {
"compilerOptions": {
"target": "ES2022",
"module": "nodenext",
"moduleResolution": "nodenext",
"moduleResolution": "node",
"types": ["node"],
"rootDir": ".",
"skipLibCheck": True,
@@ -29,7 +28,6 @@ ts_project(
deps = [
"//adev:node_modules/@angular/docs",
"//adev:node_modules/@types/node",
"//adev/shared-docs/pipeline/shared:heading",
"//adev/src/app/routing/navigation-entries",
"//adev/src/content/reference/errors:route-nav-items",
"//adev/src/content/reference/extended-diagnostics:route-nav-items",
+9 -2
View File
@@ -6,8 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {ALL_ITEMS} from '../../src/app/routing/navigation-entries/index.js';
import {getIdFromHeading} from '../../shared-docs/pipeline/shared/heading.mjs';
import {ALL_ITEMS} from '../../src/app/routing/navigation-entries';
import {NavigationItem} from '@angular/docs';
import {writeFileSync, readFileSync} from 'fs';
import {join, resolve} from 'path';
@@ -73,3 +72,11 @@ function main() {
}
main();
// TODO: refactor so this function is shared with the generation pipeline (adev/shared-docs/pipeline/shared/marked/transformations/heading.mts)
function getIdFromHeading(heading: string): string {
return heading
.toLowerCase()
.replace(/\s|\//g, '-') // replace spaces and slashes with dashes
.replace(/[^\p{L}\d\-]/gu, ''); // only keep letters, digits & dashes
}
@@ -137,14 +137,14 @@ dialog {
.docs-search-results__start-typing,
.docs-search-results__no-results {
padding: 0.75rem;
color: var(--quaternary-contrast);
color: var(--gray-400);
}
.docs-search-footer {
display: flex;
align-items: center;
justify-content: space-between;
color: var(--quaternary-contrast);
color: var(--gray-400);
padding: 1rem;
font-size: 0.75rem;
font-weight: 500;
@@ -95,6 +95,9 @@ export class SearchDialog {
if (!this.dialog().nativeElement.open) {
this.dialog().nativeElement.showModal?.();
}
// We want to select the pre-existing text on opening
// In order to change the search input with minimal user interaction.
this.searchForm().focusBoundControl();
},
});
@@ -28,10 +28,10 @@ import {IconComponent} from '../icon/icon.component';
class: 'docs-form-element',
},
})
export class TextField implements FormValueControl<string> {
export class TextField implements FormValueControl<string | null> {
readonly input = viewChild.required<ElementRef<HTMLInputElement>>('inputRef');
readonly name = input<string>('');
readonly value = model<string>('');
readonly value = model<string | null>(null);
readonly placeholder = input<string | null>(null);
readonly disabled = model<boolean>(false);
readonly hideIcon = input<boolean>(false);
@@ -143,7 +143,7 @@
height: fit-content;
docs-icon {
color: var(--quaternary-contrast);
color: var(--gray-400);
transition: color 0.3s ease;
}
@@ -87,7 +87,7 @@
fill="none"
>
<path
fill="var(--quaternary-contrast)"
fill="var(--gray-400)"
d="M3.4 22 2 20.6 8.6 14H4v-2h8v8h-2v-4.6L3.4 22ZM12 12V4h2v4.6L20.6 2 22 3.4 15.4 10H20v2h-8Z"
/>
</svg>
@@ -2,11 +2,11 @@
.docs-example-viewer-preview {
.docs-dark-mode & {
color: var(--primary-contrast);
background: var(--primary-constrast);
background: var(--gray-900);
}
@media screen and (prefers-color-scheme: dark) {
color: var(--primary-contrast);
background: var(--primary-constrast);
background: var(--gray-900);
}
.docs-light-mode & {
background: var(--page-background);
@@ -70,7 +70,7 @@
cursor: pointer;
height: 24px;
width: 24px;
color: var(--quaternary-contrast);
color: var(--gray-400);
path {
transition: fill 0.3s ease;
+9 -9
View File
@@ -2,14 +2,14 @@
"name": "@angular/docs",
"version": "0.0.0-PLACEHOLDER",
"peerDependencies": {
"@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",
"@angular/cdk": "^21.1.0-next",
"@angular/common": "^21.1.0-next",
"@angular/core": "^21.1.0-next",
"@angular/forms": "^21.1.0-next",
"@angular/material": "^21.1.0-next",
"@angular/platform-browser": "^21.1.0-next",
"@angular/router": "^21.1.0-next",
"@angular/ssr": "^21.1.0-next",
"algoliasearch": "^5.0.0",
"rxjs": "^7.8.1"
},
@@ -18,7 +18,7 @@
"diff": "~8.0.0",
"emoji-regex": "~10.6.0",
"fflate": "^0.8.2",
"jsdom": "~28.0.0",
"jsdom": "~27.4.0",
"marked": "~17.0.0",
"mermaid": "^11.0.0",
"shiki": "^3.0.0",
@@ -10,15 +10,6 @@ ts_project(
],
)
ts_project(
name = "heading",
srcs = ["heading.mts"],
visibility = [
"//adev/scripts/routes:__subpackages__",
"//adev/shared-docs/pipeline:__subpackages__",
],
)
ts_project(
name = "shiki",
srcs = ["shiki.mts"],
@@ -45,17 +36,3 @@ zoneless_jasmine_test(
name = "linking_test",
data = [":linking_test_lib"],
)
ts_project(
name = "heading_test_lib",
testonly = True,
srcs = ["test/heading.spec.mts"],
deps = [
":heading",
],
)
zoneless_jasmine_test(
name = "heading_test",
data = [":heading_test_lib"],
)
@@ -1,29 +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
*/
/**
* Extracts the ID from a heading text.
* Supports custom ID syntax: `## My Heading {#custom-id}`
*/
export function getIdFromHeading(heading: string): string {
// extract the extended markdown heading id
// ex: ## MyHeading {# myId}
// This is recommended in case we end up having duplicate Ids but we still want the same heading text.
// We don't want to make Id generation stateful/too complex to handle duplicates automatically.
const customIdRegex = /{#\s*([\w-]+)\s*}/g;
const customId = customIdRegex.exec(heading)?.[1];
if (customId) {
return customId;
}
return heading
.toLowerCase()
.replace(/\s|\//g, '-') // replace spaces and slashes with dashes
.replace(/[^\p{L}\d\-]/gu, ''); // only keep letters, digits & dashes
}
@@ -30,8 +30,6 @@ const LINK_EXEMPT = new Set([
'Event',
'form',
'type',
'Host',
'filter',
]);
export function shouldLinkSymbol(symbol: string): boolean {
@@ -18,7 +18,6 @@ ts_project(
"//adev:node_modules/mermaid",
"//adev:node_modules/playwright-core",
"//adev:node_modules/shiki",
"//adev/shared-docs/pipeline/shared:heading",
"//adev/shared-docs/pipeline/shared:linking",
"//adev/shared-docs/pipeline/shared:shiki",
"//adev/shared-docs/pipeline/shared/regions",
@@ -21,7 +21,7 @@ interface DocsCardContainerToken extends Tokens.Generic {
// Capture group 1: all attributes on the opening tag
// Capture group 2: all content between the open and close tags
const cardContainerRule =
/^\s*<docs-card-container(?:\s([^>]*))?>((?:.(?!\/docs-card-container))*)<\/docs-card-container>/s;
/^[^<]*<docs-card-container(?:\s([^>]*))?>((?:.(?!\/docs-card-container))*)<\/docs-card-container>/s;
const headerTitleRule = /headerTitle="([^"]*)"/;
const headerImgSrcRule = /headerImgSrc="([^"]*)"/;
@@ -23,7 +23,7 @@ interface DocsCardToken extends Tokens.Generic {
// Capture group 1: all attributes on the opening tag
// Capture group 2: all content between the open and close tags
const cardRule = /^\s*<docs-card(?:\s([^>]*))?>((?:.(?!\/docs-card))*)<\/docs-card>/s;
const cardRule = /^[^<]*<docs-card(?:\s([^>]*))?>((?:.(?!\/docs-card))*)<\/docs-card>/s;
const titleRule = /title="([^"]*)"/;
const linkRule = /link="([^"]*)"/;
@@ -41,7 +41,6 @@ export const docsCodeBlockExtension = {
const headerRule = /header\s*:\s*(['"`])([^'"`]+)\1/; // The 2nd capture matters here
const highlightRule = /highlight\s*:\s*(.*)([^,])/;
const hideCopyRule = /hideCopy/;
const hideDollarRule = /hideDollar/;
const preferRule = /\b(prefer|avoid)\b/;
const linenumsRule = /linenums/;
@@ -53,7 +52,6 @@ export const docsCodeBlockExtension = {
header: headerRule.exec(metadataStr)?.[2],
highlight: highlightRule.exec(metadataStr)?.[1],
hideCopy: hideCopyRule.test(metadataStr),
hideDollar: hideDollarRule.test(metadataStr),
style: preferRule.exec(metadataStr)?.[1] as 'prefer' | 'avoid' | undefined,
linenums: linenumsRule.test(metadataStr),
};
@@ -32,7 +32,6 @@ const visibleLinesRule = /visibleLines="([^"]*)"/;
const regionRule = /region="([^"]*)"/;
const previewRule = /preview/;
const hideCodeRule = /hideCode/;
const hideDollarRule = /hideDollar/;
const preferRule = /\b(prefer|avoid)\b/;
export const docsCodeExtension = {
@@ -53,9 +52,8 @@ export const docsCodeExtension = {
const language = languageRule.exec(attr);
const visibleLines = visibleLinesRule.exec(attr);
const region = regionRule.exec(attr);
const preview = previewRule.test(attr);
const hideCode = hideCodeRule.test(attr);
const hideDollar = hideDollarRule.test(attr);
const preview = previewRule.exec(attr) ? true : false;
const hideCode = hideCodeRule.exec(attr) ? true : false;
const classes = classRule.exec(attr);
const style = preferRule.exec(attr);
@@ -80,7 +78,6 @@ export const docsCodeExtension = {
region: region?.[1],
preview: preview,
hideCode,
hideDollar,
style: style?.[1] as 'prefer' | 'avoid' | undefined,
classes: classes?.[1]?.split(' '),
};
@@ -39,8 +39,6 @@ export interface CodeToken extends Tokens.Generic {
highlight?: string;
/** Whether to hide the copy button */
hideCopy?: boolean;
/** Whether to hide the dollar sign in the shell code */
hideDollar?: boolean;
// additional classes for the element
classes?: string[];
@@ -134,10 +132,6 @@ function applyContainerAttributesAndClasses(el: Element, token: CodeToken) {
if (token.hideCopy) {
el.setAttribute('hideCopy', 'true');
}
if (token.hideDollar) {
el.setAttribute('hideDollar', 'true');
}
const language = token.language;
if (language === 'mermaid') {
@@ -7,7 +7,7 @@
*/
import {Token, Tokens, RendererThis, TokenizerThis} from 'marked';
import {anchorTarget} from '../../helpers.mjs';
import {anchorTarget, isExternalLink} from '../../helpers.mjs';
import {AdevDocsRenderer} from '../../renderer.mjs';
interface DocsPillToken extends Tokens.Generic {
@@ -70,7 +70,11 @@ export const docsPillExtension = {
return `
<a class="docs-pill" href="${token.href}"${targetAttr}${downloadAttr}>
${this.parser.parseInline(token.tokens)}
${this.parser.parseInline(token.tokens)}${
isExternalLink(token.href)
? '<docs-icon class="docs-icon-small">open_in_new</docs-icon>'
: ''
}
</a>
`;
},
@@ -1,39 +1,4 @@
Introductory paragraph that should be preserved before any card containers.
## First Section
This section contains important information about the feature status.
### Available features
- Feature one
- Feature two
- Feature three
### In progress
The following items are currently being developed:
1. Item A with description
2. Item B with description
<docs-card-container>
<docs-card title="Card One"></docs-card>
<docs-card title="Card Two"></docs-card>
<docs-card></docs-card>
<docs-card></docs-card>
</docs-card-container>
### Additional context
More text after the first card container that should also be preserved.
## Second Section
Another paragraph before the second card container.
<docs-card-container>
<docs-card title="Card Three"></docs-card>
</docs-card-container>
## Final Section
Concluding remarks at the end of the document.
@@ -20,60 +20,10 @@ describe('markdown to html', () => {
markdownDocument = JSDOM.fragment(await parseMarkdown(markdownContent, rendererContext));
});
it('creates multiple card containers with correct card counts', () => {
const containers = markdownDocument.querySelectorAll('.docs-card-grid');
it('creates card containers containing multiple cards', () => {
const containerEl = markdownDocument.querySelector('.docs-card-grid');
expect(containers.length).toBe(2);
expect(containers[0].children.length).toBe(2);
expect(containers[1].children.length).toBe(1);
});
it('preserves all h2 section headings', () => {
const h2Elements = markdownDocument.querySelectorAll('h2');
expect(h2Elements.length).toBe(3);
expect(h2Elements[0].textContent).toContain('First Section');
expect(h2Elements[1].textContent).toContain('Second Section');
expect(h2Elements[2].textContent).toContain('Final Section');
});
it('preserves all h3 subsection headings outside card containers', () => {
// Card titles also render as h3, so we filter to only h3s not inside card containers
const allH3s = Array.from(markdownDocument.querySelectorAll('h3'));
const sectionH3s = allH3s.filter((h3) => !h3.closest('.docs-card-grid'));
expect(sectionH3s.length).toBe(3);
expect(sectionH3s[0].textContent).toContain('Available features');
expect(sectionH3s[1].textContent).toContain('In progress');
expect(sectionH3s[2].textContent).toContain('Additional context');
});
it('preserves unordered lists before card containers', () => {
const ulElements = markdownDocument.querySelectorAll('ul');
expect(ulElements.length).toBeGreaterThanOrEqual(1);
expect(ulElements[0].children.length).toBe(3);
expect(ulElements[0].textContent).toContain('Feature one');
});
it('preserves ordered lists before card containers', () => {
const olElements = markdownDocument.querySelectorAll('ol');
expect(olElements.length).toBe(1);
expect(olElements[0].children.length).toBe(2);
expect(olElements[0].textContent).toContain('Item A');
});
it('preserves paragraphs throughout the document', () => {
const paragraphs = markdownDocument.querySelectorAll('p');
expect(paragraphs.length).toBeGreaterThanOrEqual(5);
const paragraphTexts = Array.from(paragraphs).map((p) => p.textContent);
expect(paragraphTexts.some((t) => t?.includes('Introductory paragraph'))).toBeTrue();
expect(paragraphTexts.some((t) => t?.includes('important information'))).toBeTrue();
expect(paragraphTexts.some((t) => t?.includes('More text after'))).toBeTrue();
expect(paragraphTexts.some((t) => t?.includes('Another paragraph'))).toBeTrue();
expect(paragraphTexts.some((t) => t?.includes('Concluding remarks'))).toBeTrue();
expect(containerEl!.children.length).toBe(2);
expect(containerEl!.classList.contains('docs-card-grid')).toBeTrue();
});
});
@@ -11,7 +11,3 @@ this is a code block
```
code block without language
```
```shell {hideDollar}
echo "hello"
```
@@ -27,10 +27,10 @@ describe('markdown to html', () => {
expect(codeBlock?.textContent?.trim()).toBe('this is a code block');
});
it('should parse all 4 code blocks', () => {
it('should parse all 3 code blocks', () => {
const codeBlocks = markdownDocument.querySelectorAll('.docs-code');
expect(codeBlocks.length).toBe(4);
expect(codeBlocks.length).toBe(3);
});
it('should deindent code blocks correctly', () => {
@@ -42,9 +42,4 @@ describe('markdown to html', () => {
const codeBlock = markdownDocument.querySelectorAll('.docs-code')[2];
expect(codeBlock).toBeDefined();
});
it('should parse the hideDollar attribute', () => {
const codeBlock = markdownDocument.querySelectorAll('.docs-code')[3];
expect(codeBlock.getAttribute('hideDollar')).toBe('true');
});
});
@@ -12,5 +12,3 @@ const form = {
state: ['']
};
</docs-code>
<docs-code hideDollar code="echo 'hello world'" />
@@ -52,9 +52,4 @@ describe('markdown to html', () => {
const codeBlock = markdownDocument.querySelectorAll('code')[4];
expect(codeBlock?.innerHTML).not.toContain('<a href="/api/animations/state">state</a>');
});
it('should parse the hideDollar attribute', () => {
const codeBlock = markdownDocument.querySelectorAll('.docs-code')[5];
expect(codeBlock.getAttribute('hideDollar')).toBe('true');
});
});
@@ -25,10 +25,11 @@ describe('markdown to html', () => {
expect(samePageEl.textContent?.trim()).toBe('Same Page');
});
it('should render external links with _blank target', () => {
it('should render external links with _blank target and iconography', () => {
const samePageEl = markdownDocument.querySelectorAll('a.docs-pill')[1];
expect(samePageEl.getAttribute('target')).toBe('_blank');
expect(samePageEl.textContent?.trim()).toBe('External Page');
expect(samePageEl.textContent?.trim()).toContain('External Page');
expect(samePageEl.querySelector('docs-icon')?.textContent).toBe('open_in_new');
});
it('should render internal links that are relative paths', () => {
@@ -104,10 +104,6 @@ describe('markdown to html', () => {
expect(h2HeaderId).toBe('my-custom-id');
expect(h2AnchorHref).toBe(`#${h2HeaderId}`);
// Verify that the custom ID syntax is removed from the displayed text
expect(h2Anchor?.textContent?.trim()).toBe('My heading');
expect(h2Anchor?.textContent).not.toContain('{#');
});
it('should be able to parse heading with a valid tag in a code block', () => {
@@ -8,7 +8,6 @@
import {Tokens} from 'marked';
import {AdevDocsRenderer} from '../renderer.mjs';
import {getIdFromHeading} from '../../heading.mjs';
export function headingRender(
this: AdevDocsRenderer,
@@ -26,11 +25,16 @@ export function headingRender(
`;
}
const link = getIdFromHeading(headingText);
// extract the extended markdown heading id
// ex: ## MyHeading {# myId}
// This is recommended in case we end up having duplicate Ids but we still want the same heading text.
// We don't want to make Id generation stateful/too complex to handle duplicates automatically.
const customIdRegex = /{#\s*([\w-]+)\s*}/g;
const customId = customIdRegex.exec(headingText)?.[1];
// Replace code backticks and remove custom ID syntax from the displayed label
let label = parsedText.replace(/`(.*?)`/g, '<code>$1</code>');
label = label.replace(/{#\s*[\w-]+\s*}/g, '').trim();
const link = customId ?? getIdFromHeading(headingText);
const label = parsedText.replace(/`(.*?)`/g, '<code>$1</code>');
const normalizedLabel = label.replace(/<\/?code>/g, '');
return `
@@ -61,3 +65,10 @@ export function getPageTitle(text: string, filePath?: string): string {
}
</div>`;
}
function getIdFromHeading(heading: string): string {
return heading
.toLowerCase()
.replace(/\s|\//g, '-') // replace spaces and slashes with dashes
.replace(/[^\p{L}\d\-]/gu, ''); // only keep letters, digits & dashes
}
@@ -1,31 +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
*/
import {getIdFromHeading} from '../heading.mjs';
describe('getIdFromHeading', () => {
it('should generate id from simple text', () => {
expect(getIdFromHeading('My Heading')).toBe('my-heading');
});
it('should generate id from text with special characters', () => {
expect(getIdFromHeading('Step 2 - Add component')).toBe('step-2---add-component');
});
it('should extract custom id when present', () => {
expect(getIdFromHeading('My Heading {#custom-id}')).toBe('custom-id');
});
it('should extract custom id ignoring surrounding spaces', () => {
expect(getIdFromHeading('My Heading {# custom-id }')).toBe('custom-id');
});
it('should prioritize custom id over text content', () => {
expect(getIdFromHeading('Duplicate Heading {#unique-id}')).toBe('unique-id');
});
});
+38 -38
View File
@@ -5,19 +5,6 @@
// as well as better human-readability
// --> https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/oklch
// Grays
$gray-1000: oklch(16.93% 0.004 285.95); // #0f0f11
$gray-900: oklch(19.37% 0.006 300.98); // #151417
$gray-800: oklch(25.16% 0.008 308.11); // #232125
$gray-700: oklch(36.98% 0.014 302.71); // #413e46
$gray-600: oklch(44% 0.019 306.08); // #55505b
$gray-500: oklch(54.84% 0.023 304.99); // #746e7c
$gray-400: oklch(70.9% 0.015 304.04); // #a39fa9
$gray-300: oklch(84.01% 0.009 308.34); // #ccc9cf
$gray-200: oklch(91.75% 0.004 301.42); // #e4e3e6
$gray-100: oklch(97.12% 0.002 325.59); // #f6f5f6
$gray-50: oklch(98.81% 0 0); // #fbfbfb
@mixin root-definitions() {
// PRIMITIVES
// Colors
@@ -60,6 +47,19 @@ $gray-50: oklch(98.81% 0 0); // #fbfbfb
--symbolic-brown: oklch(49.06% 0.128 46.41); // #994411
--symbolic-lime: oklch(70.33% 0.2078857836035299 135.66843631046476); // #5dba00
// Grays
--gray-1000: oklch(16.93% 0.004 285.95); // #0f0f11
--gray-900: oklch(19.37% 0.006 300.98); // #151417
--gray-800: oklch(25.16% 0.008 308.11); // #232125
--gray-700: oklch(36.98% 0.014 302.71); // #413e46
--gray-600: oklch(44% 0.019 306.08); // #55505b
--gray-500: oklch(54.84% 0.023 304.99); // #746e7c
--gray-400: oklch(70.9% 0.015 304.04); // #a39fa9
--gray-300: oklch(84.01% 0.009 308.34); // #ccc9cf
--gray-200: oklch(91.75% 0.004 301.42); // #e4e3e6
--gray-100: oklch(97.12% 0.002 325.59); // #f6f5f6
--gray-50: oklch(98.81% 0 0); // #fbfbfb
// GRADIENTS
--red-to-pink-horizontal-gradient: linear-gradient(
90deg,
@@ -124,7 +124,7 @@ $gray-50: oklch(98.81% 0 0); // #fbfbfb
--pink-to-white-horizontal-gradient: linear-gradient(
90deg,
var(--vivid-pink) 0%,
#{$gray-200} 60%
var(--gray-200) 60%
);
--light-pink-to-light-purple-horizontal-gradient: linear-gradient(
@@ -166,7 +166,7 @@ $gray-50: oklch(98.81% 0 0); // #fbfbfb
--black-to-gray-vertical-gradient: linear-gradient(
0deg,
var(--primary-contrast) 0%,
#{$gray-400} 100%
var(--gray-400) 100%
);
--red-to-pink-vertical-gradient: linear-gradient(0deg, var(--hot-red) 0%, var(--vivid-pink) 100%);
@@ -217,35 +217,35 @@ $gray-50: oklch(98.81% 0 0); // #fbfbfb
// LIGHT MODE is default
// contrast - light mode
--full-contrast: black;
--primary-contrast: #{$gray-900};
--secondary-contrast: #{$gray-800};
--tertiary-contrast: #{$gray-700};
--quaternary-contrast: #{$gray-500};
--quinary-contrast: #{$gray-300};
--senary-contrast: #{$gray-200};
--septenary-contrast: #{$gray-100};
--octonary-contrast: #{$gray-50};
--primary-contrast: var(--gray-900);
--secondary-contrast: var(--gray-800);
--tertiary-contrast: var(--gray-700);
--quaternary-contrast: var(--gray-500);
--quinary-contrast: var(--gray-300);
--senary-contrast: var(--gray-200);
--septenary-contrast: var(--gray-100);
--octonary-contrast: var(--gray-50);
--page-background: white;
// Home page
// for the "unfilled" portion of the word that hasn't
// been highlighted by the gradient
--gray-unfilled: #{$gray-400};
--webgl-page-background: var(var(--page-background)) --webgl-gray-unfilled: #{$gray-400};
--gray-unfilled: var(--gray-400);
--webgl-page-background: var(var(--page-background)) --webgl-gray-unfilled: var(--gray-400);
}
@mixin dark-mode-definitions() {
// Contrasts
--full-contrast: white;
--primary-contrast: #{$gray-50};
--secondary-contrast: #{$gray-300};
--tertiary-contrast: #{$gray-300};
--quaternary-contrast: #{$gray-400};
--quinary-contrast: #{$gray-500};
--senary-contrast: #{$gray-700};
--septenary-contrast: #{$gray-800};
--octonary-contrast: #{$gray-900};
--page-background: #{$gray-1000};
--primary-contrast: var(--gray-50);
--secondary-contrast: var(--gray-300);
--tertiary-contrast: var(--gray-300);
--quaternary-contrast: var(--gray-400);
--quinary-contrast: var(--gray-500);
--senary-contrast: var(--gray-700);
--septenary-contrast: var(--gray-800);
--octonary-contrast: var(--gray-900);
--page-background: var(--gray-1000);
--bright-blue: color-mix(in srgb, oklch(51.01% 0.274 263.83), var(--full-contrast) 60%);
--indigo-blue: color-mix(in srgb, oklch(51.64% 0.229 281.65), var(--full-contrast) 70%);
@@ -260,7 +260,7 @@ $gray-50: oklch(98.81% 0 0); // #fbfbfb
--orange-red: color-mix(in srgb, oklch(63.32% 0.24 31.68), var(--full-contrast) 60%);
--super-green: color-mix(in srgb, oklch(79.12% 0.257 155.13), var(--full-contrast) 70%);
--light-vivid-pink-to-light-electric-violet-horizontal-gradient:
linear-gradient(hsl(from #{$gray-1000} h s l / 0.9), hsl(from #{$gray-1000} h s l / 0.9)),
linear-gradient(hsl(from var(--gray-1000) h s l / 0.9), hsl(from var(--gray-1000) h s l / 0.9)),
linear-gradient(
hsl(from var(--full-contrast) h s l / 0.2),
hsl(from var(--full-contrast) h s l / 0.2)
@@ -319,9 +319,9 @@ $gray-50: oklch(98.81% 0 0); // #fbfbfb
);
// Home page - dark mode
--gray-unfilled: #{$gray-700};
--webgl-page-background: #{$gray-1000};
--webgl-gray-unfilled: #{$gray-700};
--gray-unfilled: var(--gray-700);
--webgl-page-background: var(--gray-1000);
--webgl-gray-unfilled: var(--gray-700);
.docs-toggle {
input {
+1 -1
View File
@@ -51,7 +51,7 @@
}
.docs-reference-category {
color: var(--quaternary-contrast);
color: var(--gray-400);
font-size: 0.875rem;
font-weight: 500;
line-height: 1.4rem;
+61 -62
View File
@@ -6,66 +6,68 @@
border-radius: 0.25rem;
margin: 1rem 0;
.docs-card-container-header {
align-items: flex-end;
border-bottom: 1px solid var(--senary-contrast);
display: flex;
justify-content: space-between;
.docs-card-container-header {
align-items: flex-end;
border-bottom: 1px solid var(--senary-contrast);
display: flex;
justify-content: space-between;
h2 {
padding: 2.5rem 1rem 2.5rem 2.5rem;
min-width: 350px;
}
.theme-fill-bg {
fill: var(--page-background);
stroke: var(--primary-contrast);
}
.theme-fill-accent-bg {
fill: var(--septenary-contrast);
stroke: var(--primary-contrast);
}
.theme-stroke-primary {
stroke: var(--primary-contrast);
}
.theme-fill-primary {
fill: var(--primary-contrast);
}
.theme-fill-secondary {
fill: var(--octonary-contrast);
}
.theme-stroke-accent {
stroke: var(--vivid-pink);
}
.theme-fill-accent {
fill: var(--vivid-pink);
stroke: var(--primary-contrast);
}
.theme-stroke-secondary {
stroke: var(--senary-contrast);
}
.theme-fill-secondary {
fill: var(--senary-contrast);
}
.header-img {
margin-top: 5px;
overflow: hidden;
margin-bottom: -5px;
}
svg {
fill-opacity: 1;
@container header (max-width: 550px) {
display: none;
h2 {
padding: 2.5rem 1rem 2.5rem 2.5rem;
min-width: 350px;
}
rect {
fill: transparent;
.theme-fill-bg {
fill: var(--page-background);
stroke: var(--primary-contrast);
}
.theme-fill-accent-bg {
fill: var(--septenary-contrast);
stroke: var(--primary-contrast);
}
.theme-stroke-primary {
stroke: var(--primary-contrast);
}
.theme-fill-primary {
fill: var(--primary-contrast);
}
.theme-fill-secondary {
fill: var(--octonary-contrast);
}
.theme-stroke-accent {
stroke: var(--vivid-pink);
}
.theme-fill-accent {
fill: var(--vivid-pink);
stroke: var(--primary-contrast);
}
.theme-stroke-secondary {
stroke: var(--senary-contrast);
}
.theme-fill-secondary {
fill: var(--senary-contrast);
}
.header-img {
margin-top: 5px;
overflow: hidden;
margin-bottom: -5px;
}
svg {
fill-opacity: 1;
@container header (max-width: 550px) {
display: none;
}
rect {
fill: transparent;
}
}
}
}
.docs-card-container-content {
margin: 1rem;
@@ -212,10 +214,7 @@
&:before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
top: 0; right: 0; bottom: 0; left: 0;
z-index: -1;
margin: -1px;
border-radius: inherit;
@@ -224,7 +223,7 @@
.docs-nav-card-title {
background: var(--pink-to-highlight-to-purple-to-blue-horizontal-gradient);
padding: 1.5rem 1.5rem 0.5rem;
padding: 1.5rem 1.5rem .5rem;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
@@ -265,11 +264,11 @@
gap: 8px;
h3 {
margin-block-end: 0.5rem;
margin-block-end: .5rem;
}
svg path {
fill: var(--quaternary-contrast);
fill: var(--gray-400);
}
}
+8 -17
View File
@@ -204,24 +204,15 @@ $code-font-size: 0.875rem;
white-space: nowrap;
}
&:not([hideDollar]) {
.shiki {
.line {
&::before {
content: '$';
padding-inline-end: 0.5rem;
}
}
.shiki .line {
&::before {
content: '$';
padding-inline-end: 0.5rem;
}
}
display: block;
.shiki {
.line {
display: block;
&.hidden {
display: none;
}
&.hidden {
display: none;
}
}
}
@@ -248,7 +239,7 @@ $code-font-size: 0.875rem;
opacity: 1;
path {
transition: fill 0.3s ease;
fill: var(--quaternary-contrast);
fill: var(--gray-400);
}
}
.docs-check {
+2 -6
View File
@@ -1,5 +1,4 @@
// Pill
@use './../links' as links;
@mixin docs-pill() {
.docs-pill {
@@ -28,11 +27,8 @@
background: color-mix(in srgb, var(--pill-accent) 15%, transparent);
}
// External link icon
&[href^='http:'],
&[href^='https:'] {
margin-left: 0rem;
@include links.external-link-with-icon();
.docs-icon-small {
margin-inline-start: 0.25rem;
}
.docs-dark-mode & {
+1 -1
View File
@@ -91,7 +91,7 @@
width: 100vw;
height: 100vh;
backdrop-filter: blur(2px);
background-color: color-mix(in srgb, var(--page-background) 5%, transparent);
background-color: color-mix(in srgb, var(--gray-1000) 5%, transparent);
z-index: 50;
visibility: hidden;
opacity: 0;
@@ -95,28 +95,21 @@
<div>
<h2>Community translations</h2>
<ul>
<!-- Sorted using Unicode Collation Algorithm (UCA) -->
<li>
<a href="https://angular.az/" title="Azərbaycanca">Azərbaycanca</a>
</li>
<li>
<a href="https://angular-docs.tr/" title="Türkçe">Türkçe</a>
</li>
<li>
<a href="https://angular-docs.ru/" title="Русский">Русский</a>
</li>
<li>
<a href="https://angular.kr/" title="한국어">한국어</a>
</li>
<li>
<a href="https://angular.jp/" title="日本語版">日本語版</a>
</li>
<li>
<a href="https://angular.cn/" title="简体中文版">简体中文版</a>
</li>
<li>
<a href="https://dev.angular.tw/" title="正體中文版">正體中文版</a>
</li>
<li>
<a href="https://angular.jp/" title="日本語版">日本語版</a>
</li>
<li>
<a href="https://angular.kr/" title="한국어">한국어</a>
</li>
<li>
<a href="https://angular-docs.ru/" title="Русский">Русский</a>
</li>
</ul>
</div>
</div>
@@ -76,6 +76,7 @@
background-color 0.3s ease,
border-color 0.3s ease;
height: 100dvh;
padding-block-start: 1rem;
padding-block-end: 2rem;
box-sizing: border-box;
// Add a subtle border for the home page
@@ -107,19 +108,6 @@
color-mix(in srgb, var(--electric-violet), transparent 70%) 25%,
color-mix(in srgb, var(--bright-blue), transparent 60%) 90%
);
&::before {
content: '';
position: absolute;
opacity: 0.8;
width: 100%;
height: 100%;
background-color: var(--octonary-contrast);
z-index: -1;
}
.adev-version-button {
border: 1px solid var(--quinary-contrast);
}
}
&.adev-nav-primary--deprecated {
@@ -165,7 +153,6 @@
list-style: none;
display: flex;
flex-direction: column;
padding-block-start: 1rem;
@include mq.for-tablet {
flex-direction: row;
@@ -159,7 +159,7 @@
.docs-delete-file,
.docs-rename-file {
docs-icon {
color: var(--quaternary-contrast);
color: var(--gray-400);
transition: color 0.3s ease;
font-size: 1.2rem;
}
@@ -270,7 +270,7 @@
border-inline-start: 1px solid var(--senary-contrast);
docs-icon {
color: var(--quaternary-contrast);
color: var(--gray-400);
transition: color 0.3s ease;
font-size: 1.3rem;
}
@@ -106,7 +106,7 @@ $width-breakpoint: 950px;
z-index: var(--z-index-content);
docs-icon {
color: var(--quaternary-contrast);
color: var(--gray-400);
margin: auto;
font-size: 1.3rem;
transition: color 0.3s ease;
@@ -120,7 +120,7 @@ $width-breakpoint: 950px;
&:disabled {
docs-icon {
color: var(--quaternary-contrast);
color: var(--gray-400);
}
}
}
@@ -3,7 +3,7 @@
.docs-dark-mode & {
.adev-embedded-editor-preview-container {
background: var(--primary-constrast);
background: var(--gray-100);
}
}
@@ -1,42 +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
*/
import {Injectable, OnDestroy} from '@angular/core';
import angularHtml from '@shikijs/langs/angular-html';
import angularTs from '@shikijs/langs/angular-ts';
import githubDark from '@shikijs/themes/github-dark';
import githubLight from '@shikijs/themes/github-light';
import {CodeToHastOptions, createHighlighterCoreSync, HighlighterCore} from 'shiki/core';
import {createOnigurumaEngine} from 'shiki/engine/oniguruma';
@Injectable({providedIn: 'root'})
export class CodeHighligher implements OnDestroy {
private cachedHighligher: HighlighterCore | undefined;
async codeToHtml(code: string, options: CodeToHastOptions): Promise<string> {
const highlighter = await this.getHighlighter();
return highlighter.codeToHtml(code, options);
}
ngOnDestroy(): void {
this.cachedHighligher?.dispose();
}
private async getHighlighter() {
if (!this.cachedHighligher) {
const engine = await createOnigurumaEngine(import('shiki/wasm'));
this.cachedHighligher = createHighlighterCoreSync({
themes: [githubLight, githubDark],
langs: [angularTs, angularHtml],
engine,
});
}
return this.cachedHighligher;
}
}
@@ -1,44 +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
*/
import {AsyncPipe} from '@angular/common';
import {ChangeDetectionStrategy, Component, computed, inject, input} from '@angular/core';
import {DomSanitizer} from '@angular/platform-browser';
import {ThemeManager} from '../../../../core/services/theme-manager.service';
import {CodeHighligher} from '../../code-highlighting/code-highlighter';
@Component({
selector: 'adev-code-block',
template: `<pre><code [innerHTML]="highlightedCode() | async"></code></pre>`,
imports: [AsyncPipe],
styles: `
::ng-deep pre {
margin: 0;
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CodeBlock {
codeHighlighter = inject(CodeHighligher);
code = input.required<string>();
language = input<'angular-html' | 'angular-ts'>('angular-ts');
sanitizer = inject(DomSanitizer);
theme = inject(ThemeManager);
highlightedCode = computed(() => {
return this.codeHighlighter
.codeToHtml(this.code(), {
cssVariablePrefix: '--shiki-',
lang: this.language(),
theme: this.theme.theme() === 'light' ? 'github-light' : 'github-dark',
})
.then((hightlightedHtml) => {
return this.sanitizer.bypassSecurityTrustHtml(hightlightedHtml);
});
});
}
@@ -1,27 +0,0 @@
<div class="two-column-step">
<div class="step-description">
<div class="header">Control Flow</div>
<div class="description">
Angular's new control flow syntax makes templates easier to write and understand.
</div>
<button
class="docs-primary-btn"
[attr.text]="'Learn more about Control Flow'"
routerLink="/guide/templates/control-flow"
>
Learn more about Control Flow
</button>
</div>
<div class="step-code">
<div class="code-block">
<div class="code-header">
<span>my-template.html</span>
<span class="copy-icon"></span>
</div>
<adev-code-block [code]="Example" language="angular-html" />
</div>
</div>
</div>
@@ -1,85 +0,0 @@
@use '@angular/docs/styles/media-queries' as mq;
.two-column-step {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 3rem;
text-align: left;
width: 100%;
max-width: 1200px;
margin: 0 auto;
box-sizing: border-box;
background-color: var(--card-background);
border: 1px solid var(--senary-contrast);
border-radius: 0.5rem;
padding: 2rem;
@include mq.for-tablet-down {
grid-template-columns: 1fr;
gap: 2rem;
padding: 1.5rem;
}
@include mq.for-phone-only {
padding: 1rem;
}
}
.step-description {
display: flex;
flex-direction: column;
gap: 1.5rem;
.header {
font-size: 1.5rem;
font-weight: 500;
}
.description {
font-size: 1.1rem;
line-height: 1.6;
color: var(--secondary-contrast);
}
}
.step-code {
display: flex;
flex-direction: column;
gap: 1.5rem;
min-width: 0;
.code-block {
background: var(--card-background);
border: 1px solid var(--senary-contrast);
border-radius: 0.5rem;
font-size: 0.9rem;
.code-header {
display: flex;
padding: 0.75rem;
background: var(--subtle-purple);
border-radius: 0.25rem 0.25rem 0 0;
transition: background 0.3s ease;
& > span {
background-image: var(--purple-to-blue-horizontal-gradient);
background-clip: text;
color: transparent;
font-size: 0.875rem;
font-style: normal;
font-weight: 400;
line-height: 1.4rem;
letter-spacing: -0.00875rem;
margin: 0;
word-wrap: break-word;
width: fit-content;
}
}
pre {
font-family: 'JetBrains Mono', monospace;
margin: 0;
color: #a9b7c6;
}
}
}
@@ -1,50 +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
*/
import {ChangeDetectionStrategy, Component} from '@angular/core';
import {RouterLink} from '@angular/router';
import {CodeBlock} from '../code-block/code-block';
@Component({
selector: 'adev-control-flow-example',
imports: [RouterLink, CodeBlock],
templateUrl: './control-flow-example.html',
styleUrls: ['./control-flow-example.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ControlFlowExample {
Example = example;
}
const example = `
@if(meal() === 'burger') {
<p>You selected a burger!</p>
} @else if(lang meal === 'pizza') {
<p>You selected a pizza!</p>
} @else {
<p>Please select a meal.</p>
}
@for(item of ShoppingList; track $index) {
<p>{{ item }}</p>
}
@switch(user.role) {
@case('user') {
<p>Welcome, {{user.name}}</p>
}
@case('moderator')
@case('admin') {
<p>Welcome</p>
}
@default {
<p>Welcome, guest!</p>
}
}
`.trim();
@@ -1,38 +0,0 @@
<div class="two-column-step">
<div class="step-description">
<div class="header">Deferrable Views</div>
<div class="description">
Deferrable views (also known as <code>&#64;defer</code> blocks) reduce the initial bundle size
of your application by deferring the loading of code that is not strictly necessary for the
initial rendering of a page.
</div>
<button
class="docs-primary-btn"
[attr.text]="'Learn more about deferred loading'"
routerLink="/guide/templates/defer"
>
Learn more about deferred loading
</button>
</div>
<div class="step-code">
<div class="code-block">
<div class="code-header">
<span>data-visualization-page.html</span>
<span class="copy-icon"></span>
</div>
<adev-code-block [code]="exampleHtml" language="angular-html" />
</div>
<div class="code-block">
<div class="code-header">
<span>data-visualization-page.ts</span>
<span class="copy-icon"></span>
</div>
<adev-code-block [code]="exampleTs" />
</div>
</div>
</div>
@@ -1,86 +0,0 @@
@use '@angular/docs/styles/media-queries' as mq;
.two-column-step {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 3rem;
text-align: left;
width: 100%;
max-width: 1200px;
margin: 0 auto;
box-sizing: border-box;
background-color: var(--card-background);
border: 1px solid var(--senary-contrast);
border-radius: 0.5rem;
padding: 2rem;
@include mq.for-tablet-down {
grid-template-columns: 1fr;
gap: 2rem;
padding: 1.5rem;
}
@include mq.for-phone-only {
padding: 1rem;
}
}
.step-description {
display: flex;
flex-direction: column;
gap: 1.5rem;
.header {
font-size: 1.5rem;
font-weight: 500;
}
.description {
font-size: 1.1rem;
line-height: 1.6;
color: var(--secondary-contrast);
}
}
.step-code {
display: flex;
flex-direction: column;
gap: 1.5rem;
min-width: 0;
.code-block {
background: var(--card-background);
border: 1px solid var(--senary-contrast);
border-radius: 0.5rem;
font-size: 0.9rem;
overflow: auto;
.code-header {
display: flex;
padding: 0.75rem;
background: var(--subtle-purple);
border-radius: 0.25rem 0.25rem 0 0;
transition: background 0.3s ease;
& > span {
background-image: var(--purple-to-blue-horizontal-gradient);
background-clip: text;
color: transparent;
font-size: 0.875rem;
font-style: normal;
font-weight: 400;
line-height: 1.4rem;
letter-spacing: -0.00875rem;
margin: 0;
word-wrap: break-word;
width: fit-content;
}
}
pre {
font-family: 'JetBrains Mono', monospace;
margin: 0;
color: #a9b7c6;
}
}
}
@@ -1,44 +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
*/
import {ChangeDetectionStrategy, Component} from '@angular/core';
import {RouterLink} from '@angular/router';
import {CodeBlock} from '../code-block/code-block';
@Component({
selector: 'adev-defer-example',
imports: [RouterLink, CodeBlock],
templateUrl: './deferrable-views-example.html',
styleUrls: ['./deferrable-views-example.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DeferrableViewsExample {
exampleHtml = exampleHtml;
exampleTs = exampleTs;
}
const exampleTs = `
@Component({
// importing the component that will be loaded lazily
imports: [DataVisualization],
})
export class DataVisualizationPage {
dataService = inject(DataService);
dataSet = this.dataService.getData();
}
`.trim();
const exampleHtml = `
<section>
<h2>Data visualization</h2>
@defer {
<!-- lazy loading a heavy component -->
<app-data-visualization [data]="dataSet">
}
</section>
`.trim();
@@ -0,0 +1,65 @@
/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
DestroyRef,
EnvironmentInjector,
inject,
input,
} from '@angular/core';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
import {forkJoin, switchMap} from 'rxjs';
import {
EmbeddedEditor,
injectEmbeddedTutorialManager,
injectNodeRuntimeSandbox,
} from '../../../editor';
@Component({
selector: 'adev-code-editor',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [EmbeddedEditor],
template: `<embedded-editor />`,
})
export class CodeEditorComponent {
private readonly cdRef = inject(ChangeDetectorRef);
private readonly environmentInjector = inject(EnvironmentInjector);
private readonly destroyRef = inject(DestroyRef);
readonly tutorialFiles = input.required<string>();
constructor() {
this.loadEmbeddedEditor();
}
private loadEmbeddedEditor() {
// If using `async-await`, `this` will be captured until the function is executed
// and completed, which can lead to a memory leak if the user navigates away from
// this component to another page.
forkJoin([
injectNodeRuntimeSandbox(this.environmentInjector),
injectEmbeddedTutorialManager(this.environmentInjector),
])
.pipe(
switchMap(([nodeRuntimeSandbox, embeddedTutorialManager]) =>
embeddedTutorialManager
.fetchAndSetTutorialFiles(this.tutorialFiles())
.then(() => nodeRuntimeSandbox),
),
takeUntilDestroyed(this.destroyRef),
)
.subscribe((nodeRuntimeSandbox) => {
this.cdRef.markForCheck();
nodeRuntimeSandbox.init();
});
}
}
@@ -1,216 +0,0 @@
<div class="header">Hydration</div>
<div class="description">
Hydration is the process that restores the server-side rendered application on the client
<p>
This demo showcases Angular's hydration strategies, allowing you to simulate network latency and
observe how different components hydrate based on their configured triggers.
</p>
</div>
<div class="demo-container">
<div class="max-width-wrapper">
<div class="main-layout">
<!-- Controls & Logs -->
<div class="sidebar">
<div class="card config-card">
<h3 class="sidebar-title font-mono">
<docs-icon>schedule</docs-icon>
Simulation Config
</h3>
<div class="config-controls">
<div class="control-group">
<div class="label-row">
<label>Network Latency</label>
<span class="latency-value font-mono">{{ latency() }}ms</span>
</div>
<input
type="range"
min="0"
max="4000"
step="100"
[value]="latency()"
(input)="updateLatency($event)"
class="range-input"
/>
</div>
<button
(click)="resetSimulation()"
[disabled]="isSimulationRunning()"
class="btn-reset"
>
<docs-icon>refresh</docs-icon>
Reset Lifecycle
</button>
</div>
</div>
<!-- Terminal Log -->
<div class="terminal">
<h3 class="terminal-title font-mono">Hydration Engine Log</h3>
<div class="terminal-body font-mono custom-scrollbar">
@for (evt of eventQueue(); track $index) {
<div class="log-entry animate-in">
<span class="log-index">0{{ $index + 1 }}</span>
<span class="log-tag">event:</span>
<span class="log-msg">{{ evt }}</span>
</div>
}
@if (eventQueue().length === 0) {
<div class="log-placeholder">Awaiting app bootstrap...</div>
}
</div>
</div>
</div>
<!-- Simulator Stage -->
<div class="simulator-stage">
<div class="browser-window">
<!-- Browser UI -->
<div class="browser-header">
<div class="window-controls">
<div class="dot-win"></div>
<div class="dot-win"></div>
<div class="dot-win"></div>
</div>
<div class="address-bar font-mono">
<docs-icon>lock</docs-icon>
angular.dev/hydration-strategies
</div>
</div>
<!-- Viewport Content -->
<div class="viewport">
@if (currentState() === 'Server-Side Rendering...') {
<div class="ssr-overlay animate-fade">
<div class="ssr-spinner">
<div class="spinner-outer"></div>
<div class="spinner-inner"></div>
</div>
<p class="ssr-title">Generating SSR Markup...</p>
</div>
}
<div class="viewport-flex">
<div class="viewport-header">
<div class="header-text">
<h4 class="viewport-title">Component Strategies</h4>
<p class="viewport-subtitle">Mixing eager and deferred hydration triggers.</p>
</div>
<div class="viewport-stats">
<div class="stats-label">Main App</div>
<div class="stats-value font-tabular">{{ progress() }}%</div>
</div>
</div>
<div class="component-grid">
@for (card of [card1(), card2(), card3(), card4()]; track $index) {
<div
(click)="handleCardClick(card.id)"
class="component-card transition-all"
[class]="{
'interactive': isInteractive() || card.strategy === 'on-interaction',
'static': !isInteractive() && card.strategy !== 'on-interaction',
'active-card': card.clickCount > 0,
'dashed-border': !card.isHydrated,
}"
>
<div class="card-top">
<div
class="icon-box transition-all"
[class]="card.isHydrated ? 'icon-hydrated' : 'icon-static'"
>
@if (card.isHydrating) {
<div class="loading-spinner"></div>
} @else {
<docs-icon>water_drop</docs-icon>
}
</div>
@if (card.clickCount > 0) {
<div class="click-badge font-tabular animate-zoom">
<span class="badge-label">CLICKS:</span> {{ card.clickCount }}
</div>
}
</div>
<div class="card-body">
<h5 class="card-title">{{ card.title }}</h5>
<p class="card-desc">{{ card.description }}</p>
</div>
<div class="card-footer">
@if (card.isHydrating) {
<div class="tag tag-deferred animate-slide-in">
<span class="tag-dot spin"></span>
Hydrating...
</div>
} @else if (!card.isHydrated) {
<div class="tag tag-deferred">
<span class="tag-dot pulse"></span>
Static
</div>
} @else {
<div class="tag tag-hydrated animate-slide-in">
<span class="tag-dot"></span>
Hydrated
</div>
}
</div>
@if (card.syntax) {
<div
class="syntax-badge font-mono"
[class]="card.isHydrated ? 'syntax-inactive' : 'syntax-active'"
>
{{ card.syntax }}
</div>
}
</div>
}
</div>
</div>
</div>
<!-- Main Lifecycle Progress -->
<div class="lifecycle-progress-bar">
<div class="progress-fill transition-all" [style.width.%]="progress()"></div>
</div>
</div>
</div>
</div>
<!-- Explanatory Feature Cards -->
<div class="explainer-grid">
<div class="explainer-item">
<h5 class="explainer-title"><span class="dot-red"></span> Eager Hydration</h5>
<p class="explainer-text">
Essential components (like navigation or top-fold content) hydrate as soon as the main
application bundle is ready.
</p>
</div>
<div class="explainer-item">
<h5 class="explainer-title"><span class="dot-violet"></span> Incremental Hydration</h5>
<p class="explainer-text">
Use hydration triggers like <code>on interaction</code> or <code>on timer</code> to delay
JS execution for non-critical elements.
</p>
</div>
<div class="explainer-item">
<h5 class="explainer-title"><span class="dot-green"></span> Event Replay</h5>
<p class="explainer-text">
User clicks are never lost. Angular records events during the "Static" phase and
re-dispatches them once hydrated.
</p>
</div>
</div>
</div>
<button
class="docs-primary-btn"
[attr.text]="'Learn more about hydration'"
routerLink="/guide/hydration"
>
Learn more about hydration
</button>
</div>
@@ -1,746 +0,0 @@
@use '@angular/docs/styles/media-queries' as mq;
:host {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 2rem 1rem;
border: 1px solid var(--senary-contrast);
border-radius: 0.5rem;
--angular-gradient: linear-gradient(135deg, var(--orange-red) 0%, var(--electric-violet) 100%);
}
.header {
font-size: 1.5rem;
font-weight: 500;
}
.description {
font-size: 1.1rem;
line-height: 1.6;
color: var(--secondary-contrast);
}
.demo-container {
color: var(--primary-contrast);
font-family:
system-ui,
-apple-system,
sans-serif;
}
.max-width-wrapper {
max-width: 1152px;
margin: 0 auto;
}
.font-mono {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
}
.font-tabular {
font-variant-numeric: tabular-nums;
}
.transition-all {
transition: all 0.3s ease-out;
}
/* Animations */
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
@keyframes slide-up {
from {
opacity: 0;
transform: translateY(2rem);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes zoom-in {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.animate {
&-spin {
animation: spin 1s linear infinite;
}
&-slide-up {
animation: slide-up 0.4s ease-out forwards;
}
&-zoom {
animation: zoom-in 0.2s ease-out forwards;
}
&-fade {
animation: fade-in 0.5s ease-out forwards;
}
}
/* Layout */
.status {
&-header {
display: flex;
justify-content: flex-end;
margin-bottom: 2rem;
}
&-pill {
display: flex;
align-items: center;
gap: 1rem;
background: var(--page-background);
border: 1px solid var(--senary-contrast);
padding: 0.5rem 1rem;
border-radius: 1rem;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
&-indicator {
display: flex;
align-items: center;
gap: 0.5rem;
}
&-text {
font-size: 11px;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--tertiary-contrast);
}
}
.dot {
width: 10px;
height: 10px;
border-radius: 50%;
&.pulse {
animation: pulse 2s infinite;
}
&.bg-zinc-300 {
background-color: var(--quinary-contrast);
}
&.bg-orange-500 {
background-color: var(--symbolic-yellow);
}
&.bg-blue-500 {
background-color: var(--bright-blue);
}
&.bg-emerald-500 {
background-color: var(--super-green);
}
}
.divider {
height: 1rem;
width: 1px;
background-color: var(--senary-contrast);
}
.progress-percentage {
font-size: 11px;
font-weight: bold;
color: var(--electric-violet);
}
.main-layout {
display: grid;
grid-template-columns: 1fr;
gap: 2rem;
@media (min-width: 1024px) {
grid-template-columns: repeat(12, 1fr);
}
}
@media (min-width: 1024px) {
.sidebar {
grid-column: span 4;
}
.simulator-stage {
grid-column: span 8;
}
}
/* Sidebar & Config */
.sidebar {
display: flex;
flex-direction: column;
gap: 2rem;
}
.card {
background: var(--page-background);
border: 1px solid var(--senary-contrast);
border-radius: 1.5rem;
padding: 1.5rem;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
}
.sidebar-title {
font-size: 12px;
font-weight: bold;
color: var(--quaternary-contrast);
text-transform: uppercase;
letter-spacing: 0.1em;
margin-bottom: 1.5rem;
margin-top: 0;
display: flex;
align-items: center;
gap: 0.5rem;
}
.config-controls {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.label-row {
display: flex;
justify-content: space-between;
margin-bottom: 0.75rem;
label {
font-size: 14px;
font-weight: bold;
}
}
.latency-value {
font-size: 14px;
color: var(--electric-violet);
font-weight: bold;
}
.range-input {
width: 100%;
height: 6px;
background: var(--septenary-contrast);
border-radius: 8px;
appearance: none;
cursor: pointer;
&::-webkit-slider-thumb {
appearance: none;
width: 18px;
height: 18px;
background: var(--electric-violet);
border: 4px solid var(--page-background);
border-radius: 50%;
box-shadow: 0 4px 6px color-mix(in srgb, var(--electric-violet), transparent 80%);
}
}
.btn-reset {
width: 100%;
padding: 1rem;
background: var(--primary-contrast);
color: var(--page-background);
border: none;
border-radius: 1rem;
font-weight: bold;
font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
transition: transform 0.1s;
&:hover {
background: var(--secondary-contrast);
}
&:active {
transform: scale(0.98);
}
&[disabled] {
pointer-events: none;
opacity: 0.5;
}
}
/* Terminal */
.terminal {
background: var(--primary-contrast);
border-radius: 1.5rem;
padding: 1.5rem;
color: var(--page-background);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
position: relative;
&-title {
font-size: 10px;
font-weight: bold;
color: var(--quinary-contrast);
text-transform: uppercase;
letter-spacing: 0.2em;
margin-bottom: 1rem;
margin-top: 0;
}
&-body {
min-height: 160px;
max-height: 220px;
overflow-y: auto;
font-size: 11px;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
}
.log {
&-entry {
display: flex;
align-items: center;
gap: 0.75rem;
}
&-index {
color: var(--senary-contrast);
}
&-tag {
color: var(--senary-contrast);
}
&-msg {
color: var(--septenary-contrast);
}
&-placeholder {
color: var(--tertiary-contrast);
font-style: italic;
}
}
/* Browser Mock */
.browser {
&-window {
background: var(--page-background);
border: 1px solid var(--senary-contrast);
border-radius: 2.5rem;
overflow: hidden;
display: flex;
flex-direction: column;
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.05);
min-height: 520px;
}
&-header {
background: var(--octonary-contrast);
border-bottom: 1px solid var(--senary-contrast);
padding: 1rem 1.5rem;
display: flex;
align-items: center;
gap: 1rem;
}
}
.window-controls {
display: flex;
gap: 6px;
}
.dot-win {
width: 10px;
height: 10px;
border-radius: 50%;
background: var(--quinary-contrast);
}
.address-bar {
flex: 1;
background: var(--page-background);
border: 1px solid var(--senary-contrast);
border-radius: 0.75rem;
padding: 0.375rem 1rem;
font-size: 11px;
color: var(--quaternary-contrast);
display: flex;
align-items: center;
gap: 0.5rem;
docs-icon {
font-size: 12px;
}
}
.viewport {
padding: 2rem;
flex: 1;
position: relative;
&-flex {
display: flex;
flex-direction: column;
height: 100%;
}
&-header {
display: flex;
margin-bottom: 1rem;
width: 100%;
}
&-title {
font-size: 24px;
font-weight: 900;
letter-spacing: -0.025em;
margin-bottom: 0.25rem;
margin-top: 0;
}
&-subtitle {
font-size: 12px;
color: var(--quaternary-contrast);
font-weight: 500;
}
&-stats {
text-align: right;
margin-left: auto;
}
}
.ssr {
&-overlay {
position: absolute;
inset: 0;
background: color-mix(in srgb, var(--page-background), transparent 5%);
z-index: 50;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
&-spinner {
position: relative;
width: 48px;
height: 48px;
margin-bottom: 1rem;
}
&-title {
font-weight: bold;
color: var(--primary-contrast);
letter-spacing: -0.01em;
}
}
.spinner {
&-outer {
position: absolute;
inset: 0;
background: var(--angular-gradient);
border-radius: 0.75rem;
opacity: 0.2;
animation: spin 1s linear infinite;
}
&-inner {
position: absolute;
inset: 8px;
background: var(--angular-gradient);
border-radius: 0.5rem;
animation: spin 1.5s linear infinite;
}
}
.stats {
&-label {
font-size: 9px;
font-weight: bold;
text-transform: uppercase;
color: var(--quaternary-contrast);
letter-spacing: 0.1em;
margin-bottom: 0.25rem;
}
&-value {
font-size: 32px;
font-weight: 900;
letter-spacing: -0.05em;
}
}
.component-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1rem;
flex: 1;
@media (min-width: 640px) {
grid-template-columns: 1fr 1fr;
}
}
/* Card States */
.component-card {
position: relative;
padding: 1.5rem;
border: 1px solid var(--senary-contrast);
border-radius: 1rem;
display: flex;
flex-direction: column;
justify-content: space-between;
user-select: none;
overflow: hidden;
&.interactive {
cursor: pointer;
&:hover {
border-color: var(--electric-violet);
box-shadow: 0 20px 25px -5px color-mix(in srgb, var(--electric-violet), transparent 95%);
}
}
&.static {
background: color-mix(in srgb, var(--octonary-contrast), transparent 50%);
border-color: transparent;
}
&.active-card {
box-shadow: inset 0 2px 4px color-mix(in srgb, var(--electric-violet), transparent 95%);
overflow: hidden;
}
&.dashed-border {
border-style: dashed;
border-color: var(--quinary-contrast);
}
}
.card {
&-top {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
&-body {
margin-top: 1rem;
}
&-title {
font-size: 18px;
font-weight: 900;
margin-bottom: 0.125rem;
}
&-desc {
font-size: 10px;
color: var(--quaternary-contrast);
font-weight: 500;
line-height: 1.25;
}
&-footer {
margin-top: 1rem;
}
}
.icon {
&-box {
width: 48px;
height: 48px;
border-radius: 1rem;
display: flex;
align-items: center;
justify-content: center;
}
&-hydrated {
background: var(--septenary-contrast);
.component-card.interactive:hover & {
background: var(--electric-violet);
color: var(--page-background);
}
}
&-static {
background: var(--octonary-contrast);
color: var(--senary-contrast);
}
}
.loading-spinner {
width: 24px;
height: 24px;
border: 2px solid var(--senary-contrast);
border-top-color: var(--electric-violet);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
.click {
&-badge {
background: var(--electric-violet);
color: var(--page-background);
padding: 0.25rem 0.625rem;
border-radius: 9999px;
font-size: 10px;
font-weight: 900;
letter-spacing: 0.05em;
display: flex;
align-items: center;
gap: 4px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
}
.badge-label {
opacity: 0.7;
}
.tag {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 9px;
font-weight: 900;
text-transform: uppercase;
letter-spacing: 0.1em;
padding: 0.25rem 0.5rem;
border-radius: 0.375rem;
width: fit-content;
&-dot {
width: 6px;
height: 6px;
border-radius: 50%;
}
&-deferred {
color: var(--symbolic-yellow);
background: color-mix(in srgb, var(--symbolic-yellow), transparent 90%);
.tag-dot {
background: var(--symbolic-yellow);
}
}
&-hydrated {
color: var(--super-green);
background: color-mix(in srgb, var(--super-green), transparent 90%);
.tag-dot {
background: var(--super-green);
}
}
}
.syntax {
&-badge {
position: absolute;
top: 0;
right: 0;
padding: 0.25rem 0.75rem;
font-size: 8px;
font-weight: bold;
border-left: 1px solid;
border-bottom: 1px solid;
border-bottom-left-radius: 0.75rem;
}
&-active {
background: color-mix(in srgb, var(--electric-violet), transparent 95%);
color: var(--electric-violet);
border-color: color-mix(in srgb, var(--electric-violet), transparent 90%);
}
&-inactive {
background: var(--septenary-contrast);
color: var(--quaternary-contrast);
border-color: var(--senary-contrast);
}
}
/* Progress bar */
.lifecycle-progress-bar {
height: 6px;
background: var(--septenary-contrast);
width: 100%;
position: relative;
}
.progress-fill {
position: absolute;
left: 0;
top: 0;
height: 100%;
background: var(--angular-gradient);
box-shadow: 0 0 10px color-mix(in srgb, var(--electric-violet), transparent 70%);
}
/* Explainer */
.explainer {
&-grid {
margin-top: 1rem;
display: grid;
grid-template-columns: 1fr;
gap: 3rem;
border-top: 1px solid var(--senary-contrast);
padding-top: 3rem;
margin-bottom: 1rem;
@media (min-width: 768px) {
grid-template-columns: repeat(3, 1fr);
}
}
&-item {
display: flex;
flex-direction: column;
gap: 1rem;
}
&-title {
font-weight: bold;
display: flex;
align-items: center;
gap: 0.75rem;
font-size: 16px;
margin: 0;
span {
width: 8px;
height: 8px;
border-radius: 50%;
}
}
&-text {
font-size: 14px;
color: var(--quaternary-contrast);
line-height: 1.6;
}
}
.dot {
&-red {
background: var(--orange-red);
}
&-violet {
background: var(--electric-violet);
}
&-green {
background: var(--super-green);
}
}
code {
background: var(--septenary-contrast);
padding: 0 4px;
border-radius: 4px;
color: var(--electric-violet);
font-family: inherit;
}
/* Scrollbar */
.custom-scrollbar {
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-track {
background: transparent;
}
&::-webkit-scrollbar-thumb {
background: var(--tertiary-contrast);
border-radius: 10px;
}
}
@@ -1,243 +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
*/
import {ChangeDetectionStrategy, Component, signal, WritableSignal} from '@angular/core';
import {IconComponent} from '@angular/docs';
import {RouterLink} from '@angular/router';
interface SimulationCard {
id: number;
clickCount: number;
penddingEvents: number;
isHydrated: boolean;
isHydrating: boolean;
strategy?: 'on-interaction' | 'on-timer';
syntax?: string;
title: string;
description: string;
}
@Component({
selector: 'adev-hydration-example',
imports: [RouterLink, IconComponent],
templateUrl: './hydration-example.html',
styleUrls: ['./hydration-example.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class HydrationExample {
latency = signal(500);
progress = signal(0);
currentState = signal<
'Server-Side Rendering...' | 'Initial Paint' | 'Loading Bundle' | 'Hydrated'
>('Server-Side Rendering...');
isInteractive = signal(false);
eventQueue = signal<string[]>([]);
isSimulationRunning = signal(false);
card1 = signal<SimulationCard>({
id: 1,
clickCount: 0,
penddingEvents: 0,
isHydrated: false,
isHydrating: true,
title: 'Eager Module',
description: 'Part of initial bundle.',
});
card2 = signal<SimulationCard>({
id: 2,
clickCount: 0,
penddingEvents: 0,
isHydrated: false,
isHydrating: true,
title: 'Eager Module',
description: 'Part of initial bundle.',
});
card3 = signal<SimulationCard>({
id: 3,
clickCount: 0,
penddingEvents: 0,
isHydrated: false,
isHydrating: false,
strategy: 'on-interaction',
syntax: '@defer (hydrate on interaction)',
title: 'Interaction Module',
description: 'Logic loads on click.',
});
card4 = signal<SimulationCard>({
id: 4,
clickCount: 0,
penddingEvents: 0,
isHydrated: false,
isHydrating: false,
strategy: 'on-timer',
syntax: '@defer (hydrate on timer(2s))',
title: 'Timer Module',
description: 'Hydrates after 2s delay.',
});
constructor() {
this.startLifecycle();
}
protected async startLifecycle() {
if (this.isSimulationRunning()) return;
this.isSimulationRunning.set(true);
this.currentState.set('Server-Side Rendering...');
this.progress.set(0);
this.isInteractive.set(false);
this.eventQueue.set([]);
const resetCard = (card: SimulationCard): SimulationCard => ({
...card,
clickCount: 0,
penddingEvents: 0,
isHydrated: false,
isHydrating: card.strategy ? false : true,
});
this.card1.update(resetCard);
this.card2.update(resetCard);
this.card3.update(resetCard);
this.card4.update(resetCard);
await this.wait(1000);
this.currentState.set('Initial Paint');
this.progress.set(20);
await this.wait(300);
this.currentState.set('Loading Bundle');
let currentP = 20;
const interval = setInterval(
() => {
currentP += 1;
if (currentP <= 100) this.progress.set(currentP);
if (currentP >= 100) clearInterval(interval);
},
Math.max(this.latency() / 80, 5),
);
await this.wait(this.latency());
this.currentState.set('Hydrated');
this.isInteractive.set(true);
this.progress.set(100);
const hydrateCard = (card: SimulationCard): SimulationCard =>
!card.strategy ? {...card, isHydrated: true, isHydrating: false} : card;
this.card1.update(hydrateCard);
this.card2.update(hydrateCard);
const pendingEvents = this.card1().penddingEvents + this.card2().penddingEvents;
await this.wait(800);
if (pendingEvents > 0) {
this.logEvent(`Replaying ${pendingEvents} queued event(s)...`);
this.processPendingEvents(this.card1);
this.processPendingEvents(this.card2);
}
await this.triggerTimerHydration(4);
this.isSimulationRunning.set(false);
}
protected async triggerTimerHydration(id: number) {
const cardSignal = this.getCardSignal(id);
await this.wait(2000);
if (!this.card4().isHydrated) {
this.logEvent(`Timer fired: Hydrating Module#4`);
cardSignal.update((c) => ({...c, isHydrating: true}));
await this.wait(800);
this.card4.update((c) => ({...c, isHydrating: false, isHydrated: true}));
if (cardSignal().penddingEvents > 0) {
this.logEvent(`Replaying ${cardSignal().penddingEvents} queued event(s)...`);
await this.wait(600);
this.processPendingEvents(cardSignal);
}
}
}
protected async handleCardClick(id: number) {
const card = this.getCardSignal(id);
if (card().isHydrated) {
this.processAction(card);
return;
}
if (card().strategy === 'on-interaction' && !card().isHydrated) {
if (!card().isHydrating) {
this.logEvent(`Manual trigger: Hydrating Module#${card().id}`);
this.logEvent(`Queued click for Module#${card().id}`);
card.update((c) => ({...c, isHydrating: true, penddingEvents: 1}));
await this.wait(600);
if (card().penddingEvents > 0) {
this.logEvent(`Replaying ${card().penddingEvents} queued event(s)...`);
}
this.processPendingEvents(card);
card.update((c) => ({...c, isHydrating: false, isHydrated: true}));
return;
}
}
if (!card().isHydrated) {
this.queueClick(card);
return;
}
}
protected updateLatency(ev: any) {
this.latency.set(parseInt(ev.target.value));
}
protected resetSimulation() {
this.startLifecycle();
}
private async queueClick(card: WritableSignal<SimulationCard>) {
card.update((c) => ({...c, penddingEvents: c.penddingEvents + 1}));
this.logEvent(`Queued click for Module#${card().id}`);
}
private processAction(card: WritableSignal<SimulationCard>) {
card.update((c) => ({...c, clickCount: c.clickCount + 1}));
}
private processPendingEvents(card: WritableSignal<SimulationCard>) {
const pending = card().penddingEvents;
card.update((c) => ({...c, penddingEvents: 0}));
for (let i = 0; i < pending; i++) {
this.processAction(card);
}
}
private logEvent(msg: string) {
this.eventQueue.update((q) => [...q, msg]);
}
private wait(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
private getCardSignal(id: number): WritableSignal<SimulationCard> {
switch (id) {
case 1:
return this.card1;
case 2:
return this.card2;
case 3:
return this.card3;
case 4:
return this.card4;
default:
throw new Error('Invalid card id');
}
}
}
@@ -1,51 +0,0 @@
<div class="two-column-step">
<div class="step-description">
<div class="header">Signals: Fine-Grained Reactivity</div>
<div class="description">
Type in the search box to filter the list. A
<code>computed</code> signal automatically recalculates the
<code>filteredItems</code> whenever the <code>searchTerm</code> signal changes.
</div>
<div class="demo-display">
<input
[value]="searchTerm()"
(input)="onSearch($event.target.value)"
placeholder="Search for fruit..."
class="demo-input"
/>
<div class="demo-list">
@for (item of filteredItems(); track item) {
<div class="demo-item">{{ item }}</div>
}
</div>
</div>
<button
class="docs-primary-btn"
[attr.text]="'Learn more about signals'"
routerLink="/guide/signals"
>
Learn more about signals
</button>
</div>
<div class="step-code">
<div class="code-block">
<div class="code-header">
<span>component.ts</span>
<span class="copy-icon"></span>
</div>
<adev-code-block [code]="tsExample" />
</div>
<div class="code-block">
<div class="code-header">
<span>component.html</span>
<span class="copy-icon"></span>
</div>
<adev-code-block [code]="htmlExample" language="angular-html" />
</div>
</div>
</div>
@@ -1,129 +0,0 @@
@use '@angular/docs/styles/media-queries' as mq;
.two-column-step {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 3rem;
text-align: left;
width: 100%;
max-width: 1200px;
margin: 0 auto;
box-sizing: border-box;
background-color: var(--card-background);
border: 1px solid var(--senary-contrast);
border-radius: 0.5rem;
padding: 2rem;
@include mq.for-tablet-down {
grid-template-columns: 1fr;
gap: 2rem;
padding: 1.5rem;
}
@include mq.for-phone-only {
padding: 1rem;
}
}
.step-description {
display: flex;
flex-direction: column;
gap: 1.5rem;
.header {
font-size: 1.5rem;
font-weight: 500;
}
.description {
font-size: 1.1rem;
line-height: 1.6;
color: var(--secondary-contrast);
}
.demo-display {
display: flex;
flex-direction: column;
gap: 1rem;
height: 250px;
margin-bottom: 2rem;
.demo-input {
width: 100%;
padding: 1rem;
border-radius: 0.5rem;
border: 1px solid var(--senary-contrast);
background: var(--septenary-contrast);
color: var(--primary-contrast);
font-size: 1rem;
box-sizing: border-box;
&:focus {
outline: 2px solid var(--primary-color);
border-color: transparent;
}
}
.demo-list {
display: flex;
flex: 1;
flex-direction: column;
gap: 0.25rem;
padding-right: 0.5rem;
border-radius: 0.5rem;
overflow-y: auto;
border: 1px solid var(--senary-contrast);
background: var(--septenary-contrast);
color: var(--primary-contrast);
.demo-item {
padding: 0.25rem 1rem;
color: var(--secondary-contrast);
border-radius: 0.5rem;
transition: background 0.2s;
margin: 0.2rem;
&:hover {
background: var(--senary-contrast);
}
}
}
}
}
.step-code {
display: flex;
flex-direction: column;
gap: 1.5rem;
min-width: 0;
.code-block {
background: var(--card-background);
border: 1px solid var(--senary-contrast);
border-radius: 0.5rem;
font-size: 0.9rem;
overflow: auto;
.code-header {
display: flex;
padding: 0.75rem;
background: var(--subtle-purple);
border-radius: 0.25rem 0.25rem 0 0;
transition: background 0.3s ease;
& > span {
background-image: var(--purple-to-blue-horizontal-gradient);
background-clip: text;
color: transparent;
font-size: 0.875rem;
font-style: normal;
font-weight: 400;
line-height: 1.4rem;
letter-spacing: -0.00875rem;
margin: 0;
word-wrap: break-word;
width: fit-content;
}
}
}
}
@@ -1,69 +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
*/
import {ChangeDetectionStrategy, Component, computed, signal} from '@angular/core';
import {RouterLink} from '@angular/router';
import {CodeBlock} from '../code-block/code-block';
@Component({
selector: 'adev-signals-demo',
imports: [RouterLink, CodeBlock],
templateUrl: './signals-demo.html',
styleUrls: ['./signals-demo.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SignalsDemo {
tsExample = tsExample;
htmlExample = htmlExample;
items = signal([
'Apple',
'Apricot',
'Avocado',
'Banana',
'Blueberry',
'Cherry',
'Date',
'Dragonfruit',
]);
searchTerm = signal('');
// A computed signal that derives the filtered list.
// It automatically re-runs when a dependency changes.
filteredItems = computed(() => {
const lowerCaseSearchTerm = this.searchTerm().toLowerCase();
return this.items().filter((item) => item.toLowerCase().includes(lowerCaseSearchTerm));
});
onSearch(searchTerm: string) {
this.searchTerm.set(searchTerm);
}
}
const tsExample = `
// Source signals for state.
items = signal(['Apple', 'Banana', /*...*/ ]);
searchTerm = signal('');
// A computed signal that derives the filtered list.
// It automatically re-runs when a dependency changes.
filteredItems = computed(() => {
const lowerCaseSearchTerm = this.searchTerm().toLowerCase();
return this.items().filter(item =>
item.toLowerCase().includes(lowerCaseSearchTerm)
);
});
`.trim();
const htmlExample = `
<input [value]="searchTerm()" (input)="searchTerm.set($event.target.value)" />
<ul>
@for (item of filteredItems(); track $index) {
<li>{{ item }}</li>
}
</ul>
`.trim();
+467 -220
View File
@@ -1,242 +1,489 @@
<main class="adev-home" #home>
<div class="banners-layer layer">
<div class="adev-banner-container">
<a href="events/v21" class="adev-banner" target="_blank">
<h1 tabindex="-1">Angular v21 is here!</h1>
<p class="adev-banner-cta">🕹️ Start your adventure</p>
</a>
</div>
</div>
<section class="logo-section">
<div class="pattern"></div>
<div class="content">
<div class="adev-banner-container">
<a href="events/v21" class="adev-banner" target="_blank">
<h1 tabindex="-1">Angular v21 is here!</h1>
<p class="adev-banner-cta">🕹️ Start your adventure</p>
</a>
<div class="search-field">
<docs-text-field placeholder="search" (click)="openSearch()" />
</div>
@if (!isUwu) {
<!-- Angular logo -->
<div class="logo-layer">
<svg class="svg" viewBox="0 0 1280 400" xmlns="http://www.w3.org/2000/svg">
<defs>
<!-- Do not remove `gradientTransform` attribute. Safari needs it in order to start the animation. -->
<linearGradient
id="gradient"
gradientUnits="userSpaceOnUse"
gradientTransform="rotate(0 640 125)"
>
<stop offset="0" stop-color="#ed0100" />
<stop offset="0.25" stop-color="#e449b4" />
<stop offset="0.5" stop-color="#ae3bfc" />
<stop offset="1" stop-color="#7f15fd" />
<animateTransform
attributeName="gradientTransform"
type="rotate"
from="0 640 125"
to="360 640 125"
dur="6.5s"
repeatCount="indefinite"
/>
</linearGradient>
</defs>
<g class="wrapper" fill="url(#gradient)">
<g class="logo">
<g class="shield">
<path
class="shield-base"
d="M311.8,101.7L302,255.6L208.3,53.8L311.8,101.7ZM246.9,300.7L176.1,341.1L105.3,300.7L246.9,300.7ZM50.1,255.6L40.4,101.7L143.9,53.8L50.1,255.6Z"
/>
<path class="shield-middle" d="M176.1,130.4L213.2,220.6L139,220.6L176.1,130.4Z" />
<path
class="shield-bottom-a-arc"
d="M106.419,301.339L105.3,300.7L119.7,265.8L232.5,265.8L246.9,300.7L245.781,301.339L106.419,301.339Z"
/>
</g>
<path
class="capt-a-letter letter"
d="M515.4,288L553.1,288L484.4,92.7L440.7,92.7L372,288L409.7,288L425.8,239.9L499.2,239.9L515.4,288ZM435.4,211.4L461.8,132.8L463.3,132.8L489.7,211.4L435.4,211.4Z"
/>
<path
class="n-letter letter"
d="M590.4,202.2L590.4,288L555.9,288L555.9,141.6L588.9,141.6L588.9,166.5L590.6,166.5C594,158.3 599.4,151.8 606.8,147C614.2,142.2 623.4,139.8 634.3,139.8C644.4,139.8 653.2,142 660.8,146.3C668.3,150.6 674.2,156.9 678.3,165.1C682.5,173.3 684.5,183.2 684.4,194.9L684.4,288L649.9,288L649.9,200.1C649.9,190.3 647.4,182.7 642.3,177.1C637.2,171.6 630.3,168.8 621.4,168.8C615.4,168.8 610,170.1 605.3,172.8C600.6,175.4 597,179.2 594.3,184.2C591.8,189.1 590.4,195.1 590.4,202.2Z"
/>
<path
class="g-letter letter"
d="M760.5,345.9C748.1,345.9 737.5,344.2 728.6,340.9C719.7,337.6 712.6,333.1 707.2,327.5C701.8,321.9 698,315.7 696,308.9L727.1,301.4C728.5,304.3 730.5,307.1 733.2,309.9C735.9,312.7 739.5,315.1 744,317C748.5,318.9 754.3,319.8 761.2,319.8C771,319.8 779.1,317.4 785.5,312.7C791.9,308 795.1,300.2 795.1,289.4L795.1,261.7L793.4,261.7C791.6,265.3 789,268.9 785.6,272.7C782.2,276.5 777.7,279.6 772.2,282.1C766.6,284.6 759.7,285.9 751.3,285.9C740.1,285.9 729.9,283.2 720.7,277.9C711.6,272.6 704.3,264.6 699,254.1C693.6,243.5 690.9,230.3 690.9,214.3C690.9,198.2 693.6,184.6 699,173.5C704.4,162.4 711.6,154 720.8,148.2C730,142.4 740.2,139.6 751.4,139.6C760,139.6 767.1,141 772.6,143.9C778.2,146.8 782.6,150.3 785.9,154.3C789.2,158.3 791.7,162.1 793.4,165.7L795.3,165.7L795.3,141.5L829.3,141.5L829.3,290.3C829.3,302.8 826.3,313.2 820.3,321.4C814.3,329.6 806.2,335.7 795.8,339.8C785.4,343.9 773.6,345.9 760.5,345.9ZM760.8,258.8C768.1,258.8 774.3,257 779.5,253.5C784.6,249.9 788.6,244.8 791.2,238.2C793.9,231.5 795.2,223.5 795.2,214.2C795.2,205 793.9,196.9 791.2,190C788.6,183.1 784.7,177.7 779.6,173.8C774.5,170 768.2,168 760.8,168C753.1,168 746.7,170 741.5,174C736.4,178 732.5,183.5 729.9,190.4C727.3,197.4 726,205.3 726,214.1C726,223.1 727.3,230.9 730,237.6C732.6,244.3 736.5,249.5 741.7,253.2C746.8,256.9 753.2,258.8 760.8,258.8Z"
/>
<path
class="u-letter letter"
d="M933.8,226.4L933.8,141.6L968.3,141.6L968.3,288L934.8,288L934.8,262L933.3,262C930,270.2 924.6,276.9 917,282.1C909.5,287.3 900.2,289.9 889.2,289.9C879.6,289.9 871.1,287.8 863.8,283.5C856.5,279.2 850.7,273 846.6,264.7C842.5,256.5 840.4,246.5 840.4,234.8L840.4,141.6L875,141.6L875,229.5C875,238.8 877.5,246.2 882.6,251.6C887.7,257.1 894.4,259.8 902.6,259.8C907.7,259.8 912.6,258.6 917.4,256.1C922.2,253.6 926.1,249.9 929.2,245C932.3,240 933.8,233.8 933.8,226.4Z"
/>
<rect class="l-letter letter" x="979.6" y="92.7" width="34.5" height="195.3" />
<path
class="a-letter letter"
d="M1068.9,290.9C1059.6,290.9 1051.3,289.2 1043.9,285.9C1036.5,282.6 1030.6,277.6 1026.4,271.1C1022.1,264.6 1020,256.5 1020,246.9C1020,238.6 1021.5,231.8 1024.6,226.4C1027.7,221 1031.8,216.7 1037.1,213.4C1042.4,210.2 1048.3,207.7 1055,206C1061.6,204.3 1068.5,203.1 1075.6,202.3C1084.2,201.4 1091.1,200.6 1096.5,199.9C1101.8,199.2 1105.7,198 1108.2,196.5C1110.6,194.9 1111.9,192.5 1111.9,189.2L1111.9,188.6C1111.9,181.4 1109.8,175.9 1105.5,171.9C1101.2,168 1095.1,166 1087.1,166C1078.6,166 1072,167.8 1067,171.5C1062.1,175.2 1058.8,179.5 1057,184.6L1024.8,180C1027.3,171.1 1031.5,163.7 1037.4,157.6C1043.2,151.6 1050.4,147.1 1058.8,144.1C1067.3,141.1 1076.6,139.6 1086.8,139.6C1093.9,139.6 1100.9,140.4 1107.9,142.1C1114.9,143.8 1121.3,146.5 1127.1,150.3C1132.9,154.1 1137.5,159.2 1141.1,165.7C1144.6,172.2 1146.4,180.3 1146.4,190L1146.4,288L1113.2,288L1113.2,267.9L1112.1,267.9C1110,272 1107.1,275.8 1103.3,279.3C1099.5,282.8 1094.8,285.7 1089.1,287.8C1083.2,289.9 1076.6,290.9 1068.9,290.9ZM1077.9,265.6C1084.8,265.6 1090.8,264.2 1095.9,261.5C1101,258.7 1104.9,255.1 1107.7,250.5C1110.5,245.9 1111.8,240.9 1111.8,235.5L1111.8,218.2C1110.7,219.1 1108.9,219.9 1106.3,220.7C1103.7,221.5 1100.9,222.1 1097.7,222.7C1094.5,223.3 1091.4,223.8 1088.3,224.2C1085.2,224.6 1082.5,225 1080.2,225.3C1075.1,226 1070.4,227.1 1066.4,228.7C1062.3,230.3 1059.1,232.5 1056.8,235.3C1054.4,238.1 1053.3,241.8 1053.3,246.2C1053.3,252.6 1055.6,257.4 1060.3,260.6C1064.8,264 1070.7,265.6 1077.9,265.6Z"
/>
<path
class="r-letter letter"
d="M1156.9,288L1156.9,141.6L1190.4,141.6L1190.4,166L1191.9,166C1194.6,157.5 1199.2,151 1205.7,146.4C1212.2,141.8 1219.7,139.5 1228.1,139.5C1230,139.5 1232.2,139.6 1234.5,139.7C1236.9,139.9 1238.9,140.1 1240.5,140.4L1240.5,172.1C1239,171.6 1236.7,171.1 1233.6,170.7C1230.5,170.3 1227.4,170.1 1224.5,170.1C1218.2,170.1 1212.6,171.5 1207.6,174.2C1202.6,176.9 1198.7,180.6 1195.8,185.4C1192.9,190.2 1191.5,195.7 1191.5,201.9L1191.5,288L1156.9,288Z"
/>
</g>
</g>
</svg>
<h2>The framework for building scalable web apps with confidence</h2>
</div>
} @else {
<!-- UwU logo -->
<div layerId="uwu" class="uwu-layer">
<img src="assets/images/uwu.png" alt="Angular logo" />
</div>
}
@if (!isUwu) {
<div class="title">
<h2>Productivity<br />Meets scalability</h2>
<h3>The framework for building scalable web apps with confidence</h3>
</div>
} @else {
<!-- UwU logo -->
<div class="uwu">
<img src="assets/images/uwu.png" alt="Angular logo" />
</div>
}
<div class="learn-angular">
<a href="#learn-more" class="adev-cta">
<button class="docs-primary-btn" [attr.text]="'Get Started'" aria-label="Get Started">
Get Started
</button>
</a>
<div class="adev-arrow"></div>
</div>
</section>
<div class="learn-angular">
<a href="#learn-more" class="adev-cta">
<button class="docs-primary-btn" [attr.text]="'Get Started'" aria-label="Get Started">
Get Started
</button>
</a>
<div class="adev-arrow"></div>
<section class="features">
<h2>Productivity meets scalability</h2>
<div class="features-grid">
<div class="feature">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g><rect fill="none" height="24" width="24" x="0" /></g>
<g>
<g>
<polygon points="19,9 20.25,6.25 23,5 20.25,3.75 19,1 17.75,3.75 15,5 17.75,6.25" />
<polygon
points="19,15 17.75,17.75 15,19 17.75,20.25 19,23 20.25,20.25 23,19 20.25,17.75"
/>
<path
d="M11.5,9.5L9,4L6.5,9.5L1,12l5.5,2.5L9,20l2.5-5.5L17,12L11.5,9.5z M9.99,12.99L9,15.17l-0.99-2.18L5.83,12l2.18-0.99 L9,8.83l0.99,2.18L12.17,12L9.99,12.99z"
/>
</g>
</g>
</svg>
<h3>AI-forward</h3>
<p>Resources and integrations to supercharge your development with AI</p>
</div>
<div class="feature">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960">
<path
d="m480-400-80-80 80-80 80 80-80 80Zm-85-235L295-735l185-185 185 185-100 100-85-85-85 85ZM225-295 40-480l185-185 100 100-85 85 85 85-100 100Zm510 0L635-395l85-85-85-85 100-100 185 185-185 185ZM480-40 295-225l100-100 85 85 85-85 100 100L480-40Z"
/>
</svg>
<h3>Opinionated & versatile</h3>
<p>Organized yet modular thanks to Angular components and dependency injection</p>
</div>
<div class="feature">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960">
<path
d="M197-197q-54-55-85.5-127.5T80-480q0-84 31.5-156.5T197-763l57 57q-44 44-69 102t-25 124q0 67 25 125t69 101l-57 57Zm113-113q-32-33-51-76.5T240-480q0-51 19-94.5t51-75.5l57 57q-22 22-34.5 51T320-480q0 33 12.5 62t34.5 51l-57 57Zm170-90q-33 0-56.5-23.5T400-480q0-33 23.5-56.5T480-560q33 0 56.5 23.5T560-480q0 33-23.5 56.5T480-400Zm170 90-57-57q22-22 34.5-51t12.5-62q0-33-12.5-62T593-593l57-57q32 32 51 75.5t19 94.5q0 50-19 93.5T650-310Zm113 113-57-57q44-44 69-102t25-124q0-67-25-125t-69-101l57-57q54 54 85.5 126.5T880-480q0 83-31.5 155.5T763-197Z"
/>
</svg>
<h3>Reactive</h3>
<p>Fast state updates with fine-grained reactivity based on Angular Signals</p>
</div>
<div class="feature">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960">
<path
d="M220-260q-92 0-156-64T0-480q0-92 64-156t156-64q37 0 71 13t61 37l68 62-60 54-62-56q-16-14-36-22t-42-8q-58 0-99 41t-41 99q0 58 41 99t99 41q22 0 42-8t36-22l310-280q27-24 61-37t71-13q92 0 156 64t64 156q0 92-64 156t-156 64q-37 0-71-13t-61-37l-68-62 60-54 62 56q16 14 36 22t42 8q58 0 99-41t41-99q0-58-41-99t-99-41q-22 0-42 8t-36 22L352-310q-27 24-61 37t-71 13Z"
/>
</svg>
<h3>Fully featured</h3>
<p>
Everything works together with Angular's first-party modules for forms, routing, and more
</p>
</div>
</div>
</section>
<section class="features-section">
<h2 id="features">Features that actually <br />help you solve problems</h2>
<!-- -->
<div ngTabs class="material-tabs">
<div ngTabList selectionMode="follow" selectedTab="signals">
<div class="tab-background"></div>
<div ngTab value="signals">Signals</div>
<div ngTab value="control-flow">Control Flow</div>
<div ngTab value="deferrable-views">Deferrable Views</div>
<div ngTab value="hydration">Hydration</div>
</div>
<div class="sliding-window">
<div ngTabPanel [preserveContent]="true" value="signals">
<ng-template ngTabContent>
@defer (on idle) {
<adev-signals-demo />
}
</ng-template>
</div>
<div ngTabPanel [preserveContent]="true" value="control-flow">
<ng-template ngTabContent>
@defer (on idle) {
<adev-control-flow-example />
}
</ng-template>
</div>
<div ngTabPanel [preserveContent]="true" value="deferrable-views">
<ng-template ngTabContent>
@defer {
<adev-defer-example />
}
</ng-template>
</div>
<div ngTabPanel [preserveContent]="true" value="hydration">
<ng-template ngTabContent>
@defer {
<adev-hydration-example />
}
</ng-template>
</div>
</div>
</div>
</section>
<section class="selling-points">
<h2>Enabling you to build<br />smarter and faster</h2>
<div class="cards">
<a class="card" routerLink="/ai/develop-with-ai">
<div class="icon-wrapper">
<docs-icon>auto_awesome</docs-icon>
</div>
<div class="card-content">
<h3>
<strong>AI-forward</strong> resources and integrations to supercharge your development
with AI
</h3>
</div>
</a>
<a class="card" routerLink="/essentials">
<div class="icon-wrapper">
<docs-icon>grid_view</docs-icon>
</div>
<div class="card-content">
<h3>
<strong>Opinionated & versatile,</strong> organized yet modular thanks to Angular
components and dependency injection
</h3>
</div>
</a>
<a class="card" routerLink="/essentials/signals">
<div class="icon-wrapper">
<docs-icon>sensors</docs-icon>
</div>
<div class="card-content">
<h3>
<strong>Reactive</strong> and fast state updates with fine-grained reactivity based on
Angular Signals
</h3>
</div>
</a>
<a class="card" routerLink="/overview">
<div class="icon-wrapper">
<docs-icon>layers</docs-icon>
</div>
<div class="card-content">
<h3>
<strong>Fully featured</strong> platform where everything works together with Angular's
first-party modules for forms, routing, and more
</h3>
</div>
</a>
</div>
</section>
<section class="performance-section" id="performance">
<div class="wrapper">
<div class="pattern"></div>
<div class="content">
<h2>Where performance matters</h2>
<h3>
<section class="performance">
<div class="section-content">
<div class="performance-half">
<h2>When performance matters</h2>
<p class="secondary">
Trusted by millions to build fast, reliable applications that scale with the size of your
team
</h3>
<button
class="docs-primary-btn"
[attr.text]="'Get started'"
routerLink="/guide/performance"
</p>
<div class="docs-card-grid">
<a routerLink="/guide/signals" class="docs-card">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 -960 960 960"
height="24px"
width="24px"
>
<path
d="M197-197q-54-55-85.5-127.5T80-480q0-84 31.5-156.5T197-763l57 57q-44 44-69 102t-25 124q0 67 25 125t69 101l-57 57Zm113-113q-32-33-51-76.5T240-480q0-51 19-94.5t51-75.5l57 57q-22 22-34.5 51T320-480q0 33 12.5 62t34.5 51l-57 57Zm170-90q-33 0-56.5-23.5T400-480q0-33 23.5-56.5T480-560q33 0 56.5 23.5T560-480q0 33-23.5 56.5T480-400Zm170 90-57-57q22-22 34.5-51t12.5-62q0-33-12.5-62T593-593l57-57q32 32 51 75.5t19 94.5q0 50-19 93.5T650-310Zm113 113-57-57q44-44 69-102t25-124q0-67-25-125t-69-101l57-57q54 54 85.5 126.5T880-480q0 83-31.5 155.5T763-197Z"
/>
</svg>
<div class="card-content">
<p>Reactivity with signals makes it a breeze to manage your state</p>
</div>
<span>Explore signals</span>
</a>
<a routerLink="/guide/routing/rendering-strategies" class="docs-card">
<svg
xmlns="http://www.w3.org/2000/svg"
height="24px"
viewBox="0 -960 960 960"
width="24px"
>
<path
d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-507h560v-133H200v133Zm0 214h560v-134H200v134Zm0 213h560v-133H200v133Zm40-454v-80h80v80h-80Zm0 214v-80h80v80h-80Zm0 214v-80h80v80h-80Z"
/>
</svg>
<div class="card-content">
<p>
Meet your performance targets with SSR, SSG, hydration, and next generation deferred
loading
</p>
</div>
<span>Learn about rendering strategies</span>
</a>
</div>
</div>
<div class="performance-half">
<svg
width="558"
height="365"
viewBox="0 0 558 365"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<!-- TODO: LINK -->
Get started
</button>
<path
d="M228.166 59.3864L234.455 85.9391C241.018 84.5704 247.58 83.2017 254.143 82.3805L250.588 55.2803C242.932 56.3752 235.549 57.7439 228.166 59.3864Z"
fill="url(#paint0_linear_194_759)"
/>
<path
d="M98.2778 145.614L120.427 161.765C124.255 156.29 128.631 151.089 133.006 146.162L112.771 127.821C107.575 133.57 102.653 139.592 98.2778 145.614Z"
fill="url(#paint1_linear_194_759)"
/>
<path
d="M63.8232 217.06L90.0743 224.451C91.715 218.155 93.9026 211.585 96.3636 205.563L70.9329 195.435C68.1984 202.552 66.0108 209.669 63.8232 217.06Z"
fill="url(#paint2_linear_194_759)"
/>
<path
d="M155.429 90.8662L170.468 113.587C175.937 110.028 181.68 106.469 187.696 103.458L174.844 79.3691C168.281 82.654 161.718 86.7601 155.429 90.8662Z"
fill="url(#paint3_linear_194_759)"
/>
<path
d="M424.502 146.435C428.877 151.363 433.252 156.564 437.081 162.038L459.23 145.888C454.855 139.866 449.933 133.843 445.011 128.095L424.502 146.435Z"
fill="url(#paint4_linear_194_759)"
/>
<path
d="M382.391 79.3691L369.539 103.458C375.281 106.469 381.023 110.028 386.766 113.587L401.806 90.8662C395.79 86.7601 389.227 82.9278 382.391 79.3691Z"
fill="url(#paint5_linear_194_759)"
/>
<path
d="M486.301 195.435L460.871 205.289C463.332 211.585 465.519 217.881 467.16 224.177L493.411 216.786C491.497 209.943 489.036 202.552 486.301 195.435Z"
fill="url(#paint6_linear_194_759)"
/>
<path
d="M363.523 13.6719L353.952 39.4034C454.581 71.1572 527.865 164.502 529.779 275.093H333.99L306.919 302.467H556.304L556.851 288.507C556.851 285.495 557.124 282.484 557.124 279.199C556.851 154.922 475.637 49.5318 363.523 13.6719Z"
fill="url(#paint7_linear_194_759)"
/>
<path
d="M227.892 274.82H27.7281C29.9157 138.224 141.483 27.6328 278.754 27.6328C285.317 27.6328 291.879 27.9065 298.169 28.454L322.506 3.81741C308.286 1.6275 293.52 0.258789 278.48 0.258789C125.349 0.258789 0.383301 125.358 0.383301 278.926C0.383301 281.937 0.383301 284.948 0.656749 288.233L1.20365 302.194H217.501L227.892 274.82Z"
fill="url(#paint8_linear_194_759)"
/>
<path
d="M341.373 13.9453L162.265 194.614L268.363 224.999L215.861 364.606L394.969 183.938L288.871 153.553L341.373 13.9453Z"
fill="url(#paint9_linear_194_759)"
/>
<defs>
<linearGradient
id="paint0_linear_194_759"
x1="224.18"
y1="87.8605"
x2="263.436"
y2="48.6467"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#F941EA" />
<stop offset="1" stop-color="#9138FF" />
</linearGradient>
<linearGradient
id="paint1_linear_194_759"
x1="102.734"
y1="158.687"
x2="131.291"
y2="130.162"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#F941EA" />
<stop offset="1" stop-color="#9138FF" />
</linearGradient>
<linearGradient
id="paint2_linear_194_759"
x1="63.1336"
y1="227.404"
x2="100.224"
y2="190.353"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#F941EA" />
<stop offset="1" stop-color="#9138FF" />
</linearGradient>
<linearGradient
id="paint3_linear_194_759"
x1="157.935"
y1="110.828"
x2="190.444"
y2="78.3545"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#F941EA" />
<stop offset="1" stop-color="#9138FF" />
</linearGradient>
<linearGradient
id="paint4_linear_194_759"
x1="426.244"
y1="161.616"
x2="461.128"
y2="126.768"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#F941EA" />
<stop offset="1" stop-color="#9138FF" />
</linearGradient>
<linearGradient
id="paint5_linear_194_759"
x1="368.074"
y1="113.184"
x2="405.694"
y2="75.6042"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#F941EA" />
<stop offset="1" stop-color="#9138FF" />
</linearGradient>
<linearGradient
id="paint6_linear_194_759"
x1="459.571"
y1="228.959"
x2="499.852"
y2="188.721"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#F941EA" />
<stop offset="1" stop-color="#9138FF" />
</linearGradient>
<linearGradient
id="paint7_linear_194_759"
x1="311.154"
y1="306.775"
x2="505.716"
y2="112.419"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#F941EA" />
<stop offset="1" stop-color="#9138FF" />
</linearGradient>
<linearGradient
id="paint8_linear_194_759"
x1="20.4484"
y1="321.544"
x2="330.571"
y2="11.7518"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#F941EA" />
<stop offset="1" stop-color="#9138FF" />
</linearGradient>
<linearGradient
id="paint9_linear_194_759"
x1="223.592"
y1="-19.7554"
x2="484.225"
y2="256.555"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#F941EA" />
<stop offset="1" stop-color="#9138FF" />
</linearGradient>
</defs>
</svg>
</div>
</div>
</section>
<section class="explore-section" id="learn-more">
<div class="title">
<h2>What to learn more about Angular?</h2>
<div class="pattern"></div>
</div>
<!-- -->
<div class="cards-grid">
<!-- Card 1: Tutorials -->
<a routerLink="/tutorials/learn-angular" class="explore-card">
<div class="card-content">
<p>
<span class="emphasis">New to Angular?</span>
Try our completely in browser tutorial lessons designed to give you hands on experience
with Angular.
</p>
<span class="fake-button">Start coding <docs-icon>arrow_forward</docs-icon></span>
<div class="editor-background"></div>
</div>
</a>
<!-- Card 2: AI -->
<a routerLink="/ai" class="explore-card">
<div class="card-content">
<p>
<span class="emphasis">Are you an LLM or curious about AI in Angular?</span>
Head over to our AI section to find prompts, strategies, best practices, examples, and
more.
</p>
<span class="fake-button">Angular + AI <docs-icon>arrow_forward</docs-icon></span>
<div class="angular-mcp-container">
<div class="mcp-content">
<div class="mcp-label">Angular MCP</div>
<div class="mcp-pills">
<span class="mcp-pill">get_best_practices</span>
<span class="mcp-pill">ai_tutor</span>
<span class="mcp-pill">find_examples</span>
</div>
</div>
<section class="learn-more">
<div class="section-content">
<h2 id="learn-more">Want to learn more?</h2>
<div class="docs-card-grid">
<a routerLink="/tutorials/learn-angular" class="docs-card">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960">
<path
d="m188.15-529.46 108.7 46.15q19.15-38.3 41.57-75.42 22.43-37.11 48.5-71.04l-75.53-14.84q-3.08-.77-5.97.19-2.88.96-5.19 3.27L188.15-529.46Zm144.77 73.69 127.62 127.62q55.46-26 104.88-59 49.42-33 84.66-68.23 75.23-75.23 111.11-154.27 35.89-79.04 41.19-188.35-109.69 5.31-188.34 41-78.66 35.69-153.89 110.92-35.23 35.24-68.23 84.85-33 49.62-59 105.46Zm229.77-102.92q-18.08-17.69-18.08-43.92t18.08-44.31Q580.77-665 607.19-665t44.5 18.08q18.08 18.08 18.08 44.31 0 26.23-18.08 43.92-18.08 18.08-44.5 18.08t-44.5-18.08Zm-29.23 374.92 112.08-111.69q2.3-2.31 3.07-5.39.77-3.07.39-5.77l-15.23-75.53q-33.93 25.69-70.85 48.11-36.92 22.42-75.61 41.96l46.15 108.31Zm314.08-659q5.53 126.39-34.97 228.19-40.5 101.81-129.11 190.04-2.54 2.54-4.88 5.08-2.35 2.54-4.89 4.69l19.69 98.85q2.62 14.61-1.69 28.03-4.31 13.43-14.54 23.66L518.08-104.54l-75.77-177.61-156.16-155.77-177.22-76.54 159.3-158.69q10.62-10.23 24.04-14.73t28.04-1.5l99.23 19.69q2.54-2.15 4.69-4.39 2.16-2.23 4.69-4.38 88.62-88.61 190.23-129.23 101.62-40.61 228.39-35.08ZM175.16-320q29.23-29.61 71.07-29.54 41.85.08 71.46 29.31 29.23 29.23 28.92 71.08-.3 41.84-29.53 71.46-23.7 23.3-72.43 38.96-48.73 15.65-134.26 26.04 10.38-85.54 25.73-134.77 15.34-49.23 39.04-72.54Zm31.61 33q-13.61 14.61-24.42 44.88-10.81 30.27-17.27 74.5 44.23-6.84 74.5-17.65 30.27-10.81 44.88-24.81 17.08-15.46 17.39-38.46.3-23-16.16-40.08-17.07-16.46-40.07-15.96T206.77-287Z"
/>
</svg>
<div class="card-content">
<h3>New to Angular?</h3>
<p>
Try our completely in browser tutorial lessons designed to give you hands on
experience with Angular.
</p>
<p class="secondary">
Recommended for developers new to Angular looking to dive right into the code
</p>
<span>Start coding</span>
</div>
</div>
</a>
<!-- Card 3: Overview -->
<a routerLink="/overview" class="explore-card">
<div class="card-content">
<p>
<span class="emphasis">Why Angular?</span>
Learn about Angular, its benefits, and if it's right for you.
</p>
<div class="logo-background"></div>
</div>
</a>
<!-- Card 4: Essentials -->
<a routerLink="/essentials" class="explore-card">
<div class="card-content">
<p>
<span class="emphasis">More of a reader?</span>
Our essentials guides are designed to help you understand Angular fundamentals in the
time it would take to finish a cup of coffee (or a tea).
</p>
<span class="fake-button">Angular concepts <docs-icon>arrow_forward</docs-icon></span>
</div>
</a>
</div>
</section>
<section class="learn-more-bottom-section" id="bottom">
<div class="pattern"></div>
<div class="content">
<h2>Want to learn more about Angular ?</h2>
<button
class="docs-primary-btn"
[attr.text]="'Learn more'"
aria-label="Learn more"
routerLink="/overview"
>
Learn more
</button>
</a>
<a routerLink="/essentials" class="docs-card">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960">
<path
d="M557.69-577.46v-38q32.62-15.15 68.27-22.73 35.66-7.58 74.04-7.58 24.08 0 46.77 3.42 22.69 3.43 45.54 9.04v35.54q-22.46-7.85-44.66-11.38-22.19-3.54-47.65-3.54-38.38 0-74.35 9.11-35.96 9.12-67.96 26.12Zm0 219.23v-39.38q31.85-14.85 68.08-22.27 36.23-7.43 74.23-7.43 24.08 0 46.77 3.43 22.69 3.42 45.54 9.03v35.54q-22.46-7.84-44.66-11.38-22.19-3.54-47.65-3.54-38.38 0-74.35 9.19-35.96 9.19-67.96 26.81Zm0-109.23v-38.77q32.62-15.15 68.27-22.73 35.66-7.58 74.04-7.58 24.08 0 46.77 3.42 22.69 3.43 45.54 9.04v35.54q-22.46-7.85-44.66-11.38-22.19-3.54-47.65-3.54-38.38 0-74.35 9.5-35.96 9.5-67.96 26.5ZM251.08-304.62q54.53 0 106.11 12.89 51.58 12.88 101.73 39.42v-417.38q-45-31.92-99.04-48.81-54.04-16.88-108.8-16.88-37.61 0-69.5 7.38t-67.73 20.23q-4.62 1.54-6.54 4.42-1.92 2.89-1.92 6.35v406.31q0 5 3.84 7.5 3.85 2.5 8.46.96 25.62-11.31 60.7-16.85 35.07-5.54 72.69-5.54Zm253.23 52.31q50.38-26.54 99.92-39.42 49.54-12.89 104.69-12.89 37.62 0 73.5 5.23 35.89 5.24 59.89 13.31 4.61 1.93 8.46-.57 3.84-2.5 3.84-7.89V-697q0-3.46-1.92-6.15-1.92-2.7-6.54-4.62-33.61-14.69-66.43-21.15-32.83-6.46-70.8-6.46-55.15 0-107.57 16.88-52.43 16.89-97.04 48.81v417.38Zm-22.89 67.69q-50.8-35.69-109.65-54.84-58.85-19.16-120.69-19.16-32.47 0-63.77 6.7-31.31 6.69-62.85 17.38-23.1 10.23-43.78-4.34T60-279.46v-420.31q0-15.77 7.77-29t22.54-19.92q37.77-16.54 78.34-24.31t82.43-7.77q62.19 0 120.75 17.39Q430.38-746 481.62-711q50.23-35 108.15-52.38 57.92-17.39 119.15-17.39 41.65 0 82.02 7.77t78.14 24.31q14.76 6.61 22.84 19.88t8.08 29.04v420.31q0 27.12-22.31 41.67-22.31 14.56-45.08 2.48-30.15-10.92-61.15-17.11-31-6.2-62.54-6.2-61.94 0-119.32 19.46-57.37 19.47-108.18 54.54ZM282.15-493.23Z"
/>
</svg>
<div class="card-content">
<h3>More of a reader?</h3>
<p>
Our essentials guides are designed to help you understand Angular fundamentals in the
time it would take to finish a cup of coffee (or tea).
</p>
<p class="secondary">
Recommended for developers with framework experience but want quick overview of
concepts
</p>
<span>Angular concepts</span>
</div>
</a>
<a routerLink="/overview" class="docs-card">
<svg class="angular-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 223 236">
<g clip-path="url(#a)">
<path
fill="url(#b)"
d="m222.077 39.192-8.019 125.923L137.387 0l84.69 39.192Zm-53.105 162.825-57.933 33.056-57.934-33.056 11.783-28.556h92.301l11.783 28.556ZM111.039 62.675l30.357 73.803H80.681l30.358-73.803ZM7.937 165.115 0 39.192 84.69 0 7.937 165.115Z"
/>
<path
fill="url(#c)"
d="m222.077 39.192-8.019 125.923L137.387 0l84.69 39.192Zm-53.105 162.825-57.933 33.056-57.934-33.056 11.783-28.556h92.301l11.783 28.556ZM111.039 62.675l30.357 73.803H80.681l30.358-73.803ZM7.937 165.115 0 39.192 84.69 0 7.937 165.115Z"
/>
</g>
<defs>
<linearGradient
id="b"
x1="49.009"
x2="225.829"
y1="213.75"
y2="129.722"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#E40035" />
<stop offset=".24" stop-color="#F60A48" />
<stop offset=".352" stop-color="#F20755" />
<stop offset=".494" stop-color="#DC087D" />
<stop offset=".745" stop-color="#9717E7" />
<stop offset="1" stop-color="#6C00F5" />
</linearGradient>
<linearGradient
id="c"
x1="41.025"
x2="156.741"
y1="28.344"
y2="160.344"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="#FF31D9" />
<stop offset="1" stop-color="#FF5BE1" stop-opacity="0" />
</linearGradient>
<clipPath id="a">
<path fill="#fff" d="M0 0h223v236H0z" />
</clipPath>
</defs>
</svg>
<div class="card-content">
<h3>Why Angular?</h3>
<p>Learn about Angular, its benefits, and if it's right for you.</p>
<p class="secondary">
Recommended for developers seeking to learn more about the Angular framework
</p>
<span>About Angular</span>
</div>
</a>
<a routerLink="/ai" class="docs-card">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<polygon points="19,9 20.25,6.25 23,5 20.25,3.75 19,1 17.75,3.75 15,5 17.75,6.25" />
<polygon
points="19,15 17.75,17.75 15,19 17.75,20.25 19,23 20.25,20.25 23,19 20.25,17.75"
/>
<path
fill="none"
stroke="currentColor"
stroke-width="1.2"
d="M 11.5 9.5 L 9 4 L 6.5 9.5 L 1 12 l 5.5 2.5 L 9 20 l 2.5 -5.5 L 17 12 L 11.5 9.5 z z"
/>
</g>
</svg>
<div class="card-content">
<h3>Are you an LLM or curious about AI in Angular?</h3>
<p>
Head over to our AI section to find prompts, strategies, best practices, examples, and
more.
</p>
<p class="secondary">
Recommended for robots... and developers who want to get the most out of Angular and
AI
</p>
<span>Angular + AI</span>
</div>
</a>
</div>
</div>
</section>
</main>
File diff suppressed because it is too large Load Diff
+1 -30
View File
@@ -6,34 +6,12 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Tab, TabContent, TabList, TabPanel, Tabs} from '@angular/aria/tabs';
import {A11yModule} from '@angular/cdk/a11y';
import {ChangeDetectionStrategy, Component, inject} from '@angular/core';
import {IS_SEARCH_DIALOG_OPEN, IconComponent, TextField} from '@angular/docs';
import {ActivatedRoute, RouterLink} from '@angular/router';
import {CodeHighligher} from './code-highlighting/code-highlighter';
import {ControlFlowExample} from './components/control-flow/control-flow-example';
import {DeferrableViewsExample} from './components/deferrable-views-example/deferrable-views-example';
import {HydrationExample} from './components/hydration-example/hydration-example';
import {SignalsDemo} from './components/signals-demo/signals-demo';
@Component({
selector: 'adev-home',
imports: [
RouterLink,
TextField,
TabList,
Tab,
Tabs,
TabPanel,
TabContent,
IconComponent,
A11yModule,
SignalsDemo,
ControlFlowExample,
DeferrableViewsExample,
HydrationExample,
],
imports: [RouterLink],
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
@@ -41,11 +19,4 @@ import {SignalsDemo} from './components/signals-demo/signals-demo';
export default class Home {
private readonly activatedRoute = inject(ActivatedRoute);
protected readonly isUwu = 'uwu' in this.activatedRoute.snapshot.queryParams;
private readonly codeHighlighter = inject(CodeHighligher);
protected readonly displaySearchDialog = inject(IS_SEARCH_DIALOG_OPEN);
openSearch() {
this.displaySearchDialog.set(true);
}
}
@@ -110,7 +110,7 @@
var(--symbolic-yellow) var(--item-attr-base-mix),
var(--octonary-contrast)
);
color: var(--page-background);
color: var(--gray-1000);
}
&.adev-experimental {
@@ -119,7 +119,7 @@
var(--symbolic-green) var(--item-attr-base-mix),
var(--octonary-contrast)
);
color: var(--page-background);
color: var(--gray-1000);
}
&.adev-deprecated {
@@ -25,7 +25,7 @@
display: flex;
align-items: center;
justify-content: flex-end;
color: var(--quaternary-contrast);
color: var(--gray-400);
background-color: var(--page-background);
border-block-start: 1px solid var(--senary-contrast);
border-radius: 0 0 0.25rem 0.25rem;
@@ -46,6 +46,8 @@ h4 {
}
.wizard {
padding-inline: 1rem;
.show-button {
display: block;
margin-block-start: 2rem;
@@ -161,15 +163,9 @@ h4 {
}
}
.adev-complexity-1 {
--badge-color: var(--super-green);
}
.adev-complexity-2 {
--badge-color: var(--bright-blue);
}
.adev-complexity-3 {
--badge-color: var(--symbolic-orange);
}
.adev-complexity-1 { --badge-color: var(--super-green); }
.adev-complexity-2 { --badge-color: var(--bright-blue); }
.adev-complexity-3 { --badge-color: var(--symbolic-orange); }
// Code blocks are generable from the markdown, we need to opt-out of the scoping
::ng-deep code {
@@ -9,8 +9,7 @@ ts_project(
tsconfig = {
"compilerOptions": {
"target": "ES2022",
"module": "nodenext",
"moduleResolution": "nodenext",
"moduleResolution": "node",
"types": ["node"],
"rootDir": ".",
"skipLibCheck": True,
@@ -341,12 +341,6 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
path: 'guide/di/di-in-action',
contentPath: 'guide/di/di-in-action',
},
{
label: 'Debugging and troubleshooting DI',
path: 'guide/di/debugging-and-troubleshooting-di',
contentPath: 'guide/di/debugging-and-troubleshooting-di',
status: 'new',
},
],
},
{
@@ -363,11 +357,6 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
path: 'guide/routing/define-routes',
contentPath: 'guide/routing/define-routes',
},
{
label: 'Route Loading Strategies',
path: 'guide/routing/loading-strategies',
contentPath: 'guide/routing/loading-strategies',
},
{
label: 'Show routes with Outlets',
path: 'guide/routing/show-routes-with-outlets',
@@ -496,13 +485,6 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
category: 'Signal Forms',
status: 'new',
},
{
label: 'Async operations',
path: 'guide/forms/signals/async-operations',
contentPath: 'guide/forms/signals/async-operations',
category: 'Signal Forms',
status: 'new',
},
{
label: 'Custom controls',
path: 'guide/forms/signals/custom-controls',
@@ -518,7 +500,7 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
status: 'new',
},
{
label: 'Migrating from Reactive Forms',
label: 'Migrating from Legacy Forms',
path: 'guide/forms/signals/migration',
contentPath: 'guide/forms/signals/migration',
category: 'Signal Forms',
@@ -1082,78 +1064,33 @@ export const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
},
{
label: 'Performance',
preserveOtherCategoryOrder: true,
children: [
{
label: 'Overview',
path: 'best-practices/performance',
contentPath: 'best-practices/performance/overview',
},
// Loading Performance
{
label: 'Lazy-loaded routes',
path: 'best-practices/performance/lazy-loaded-routes',
contentPath: 'guide/routing/loading-strategies',
category: 'Loading Performance',
},
{
label: 'Deferred loading with @defer',
path: 'best-practices/performance/defer',
contentPath: 'guide/templates/defer',
category: 'Loading Performance',
},
{
label: 'Image optimization',
path: 'best-practices/performance/image-optimization',
contentPath: 'guide/image-optimization',
category: 'Loading Performance',
},
{
label: 'Server-side rendering',
path: 'best-practices/performance/ssr',
contentPath: 'guide/ssr',
category: 'Loading Performance',
},
// Runtime Performance
{
label: 'Overview',
path: 'best-practices/runtime-performance',
contentPath: 'best-practices/runtime-performance/overview',
category: 'Runtime Performance',
},
{
label: 'Zoneless',
path: 'guide/zoneless',
contentPath: 'guide/zoneless',
category: 'Runtime Performance',
},
{
label: 'Slow computations',
path: 'best-practices/slow-computations',
contentPath: 'best-practices/runtime-performance/slow-computations',
category: 'Runtime Performance',
},
{
label: 'Skipping component subtrees',
path: 'best-practices/skipping-subtrees',
contentPath: 'best-practices/runtime-performance/skipping-subtrees',
category: 'Runtime Performance',
},
{
label: 'Zone pollution',
path: 'best-practices/zone-pollution',
contentPath: 'best-practices/runtime-performance/zone-pollution',
category: 'Runtime Performance',
},
{
label: 'Chrome DevTools profiling',
label: 'Slow computations',
path: 'best-practices/slow-computations',
contentPath: 'best-practices/runtime-performance/slow-computations',
},
{
label: 'Skipping component subtrees',
path: 'best-practices/skipping-subtrees',
contentPath: 'best-practices/runtime-performance/skipping-subtrees',
},
{
label: 'Profiling with the Chrome DevTools',
path: 'best-practices/profiling-with-chrome-devtools',
contentPath: 'best-practices/runtime-performance/profiling-with-chrome-devtools',
category: 'Runtime Performance',
},
{label: 'Zoneless', path: 'guide/zoneless', contentPath: 'guide/zoneless'},
],
},
{
-1
View File
@@ -10,7 +10,6 @@ copy_to_directory(
"//adev/src/content/ai",
"//adev/src/content/aria:aria_docs",
"//adev/src/content/best-practices",
"//adev/src/content/best-practices/performance",
"//adev/src/content/best-practices/runtime-performance",
"//adev/src/content/cdk:cdk_docs",
"//adev/src/content/cli:cli_docs",
@@ -1 +0,0 @@
<svg xmlns='http://www.w3.org/2000/svg' width='489.63' height='453.44' viewBox='4.516 2.755 489.63 453.44'><filter id='filter3_n_5746_17015' x='339.133' y='4855' width='490.001' height='453.444' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'><feFlood flood-opacity='0' result='BackgroundImageFix'/><feBlend mode='normal' in='SourceGraphic' in2='BackgroundImageFix' result='shape'/><feTurbulence type='fractalNoise' baseFrequency='4.6613855361938477 4.6613855361938477' stitchTiles='stitch' numOctaves='3' result='noise' seed='9369'/><feColorMatrix in='noise' type='luminanceToAlpha' result='alphaNoise'/><feComponentTransfer in='alphaNoise' result='coloredNoise1'><feFuncA type='discrete' tableValues='1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 '/></feComponentTransfer><feComposite operator='in' in2='shape' in='coloredNoise1' result='noise1Clipped'/><feFlood flood-color='rgba(0, 0, 0, 0.25)' result='color1Flood'/><feComposite operator='in' in2='noise1Clipped' in='color1Flood' result='color1'/><feMerge result='effect1_noise_5746_17015'><feMergeNode in='shape'/><feMergeNode in='color1'/></feMerge></filter><g xmlns='http://www.w3.org/2000/svg' filter='url(#filter3_n_5746_17015)' opacity='.5' transform='translate(-334.617 -4852.245)'><path fill='#121114' d='M828.763 5154.15 589.175 4855l172.14 72.78z'/><mask id='a' width='240' height='300' x='589' y='4855' maskUnits='userSpaceOnUse' style='mask-type: luminance'><path fill='#fff' d='M828.763 5154.15 589.175 4855l172.14 72.78z'/></mask><g mask='url(#a)'><mask id='b' width='240' height='300' x='589' y='4855' maskUnits='userSpaceOnUse' style='mask-type: luminance'><path fill='#fff' d='M828.763 5154.15 589.175 4855l172.14 72.78z'/></mask><g mask='url(#b)'><path fill='#9d43f7' d='M828.763 5154.15 589.175 4855l172.14 72.78z'/></g></g><path fill='#33264e' d='m828.762 5154.15-20.165 27.05-239.588-299.15 20.166-27.05z'/><path fill='#381e37' d='m664.565 5096.27-20.154 27.05-114.121 7.97 20.165-27.04z'/><path fill='#121114' d='m550.455 5104.25 16.865-139.15 97.245 131.17z'/><mask id='c' width='115' height='140' x='550' y='4965' maskUnits='userSpaceOnUse' style='mask-type: luminance'><path fill='#fff' d='m550.455 5104.25 16.865-139.15 97.245 131.17z'/></mask><g mask='url(#c)'><mask id='d' width='115' height='140' x='550' y='4965' maskUnits='userSpaceOnUse' style='mask-type: luminance'><path fill='#fff' d='m550.455 5104.25 16.865-139.15 97.245 131.17z'/></mask><g mask='url(#d)'><path fill='#f85de3' d='m550.455 5104.25 16.865-139.15 97.245 131.17z'/></g></g><path fill='#33264e' d='m550.455 5104.25-20.165 27.04 16.864-139.14 20.166-27.05z'/><path fill='#1a1a1a' d='m752.201 5212.53-20.166 27.05-90.643 68.86 20.165-27.05z'/><path fill='#121114' d='m541.015 5174.27 173.48-12.13 37.706 50.39-90.643 68.86-127.145-53.63z'/><mask id='e' width='219' height='120' x='534' y='5162' maskUnits='userSpaceOnUse' style='mask-type: luminance'><path fill='#fff' d='m541.015 5174.27 173.48-12.13 37.706 50.39-90.643 68.86-127.145-53.63z'/></mask><g mask='url(#e)'><path fill='#f759ce' d='m661.558 5281.39-127.145-53.63 6.602-53.49 173.48-12.13 37.706 50.39z'/></g><path fill='#171218' d='m661.557 5281.39-20.165 27.05-127.145-53.64 20.166-27.04z'/><path fill='#1a131a' d='m534.413 5227.76-20.166 27.04 6.602-53.48 20.165-27.05z'/><path fill='#121114' d='m426.746 5182.26-67.447-226.38 122.785-93.39z'/><mask id='f' width='124' height='321' x='359' y='4862' maskUnits='userSpaceOnUse' style='mask-type: luminance'><path fill='#fff' d='m426.746 5182.26-67.447-226.38 122.785-93.39z'/></mask><g mask='url(#f)'><mask id='g' width='124' height='321' x='359' y='4862' maskUnits='userSpaceOnUse' style='mask-type: luminance'><path fill='#fff' d='m482.084 4862.49-55.338 319.77-67.447-226.38z'/></mask><g mask='url(#g)'><path fill='#852c49' d='m482.084 4862.49-55.338 319.77-67.447-226.38z'/></g></g><path fill='#311822' d='m426.747 5182.26-20.166 27.05-67.448-226.38 20.166-27.05z'/></g></svg>

Before

Width:  |  Height:  |  Size: 4.0 KiB

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