Compare commits

..

85 Commits

Author SHA1 Message Date
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
1694 changed files with 85327 additions and 108689 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.
-48
View File
@@ -1,48 +0,0 @@
name: 'VSCode Angular Language Service extension'
description: Report an issue or suggest a feature for the VSCode Angular Language Service extension
body:
- type: checkboxes
id: bug-report-or-feature-request
attributes:
label: Is this a bug report or a feature request?
description: Select one.
options:
- label: Bug Report
- label: Feature Request
- type: textarea
id: reproduction-steps
attributes:
label: Please provide the steps to reproduce the issue [Bug Report only]
- type: textarea
id: expected-vs-actual-behavior
attributes:
label: Please provide the expected behavior vs the actual behavior you encountered [Bug Report only]
- type: textarea
id: screenshot
attributes:
label: Please provide a screenshot if possible [Bug Report only]
- type: textarea
id: exception-or-error
attributes:
label: Please provide the exception or error you saw [Bug Report only]
render: true
- type: textarea
id: description
attributes:
label: Description [Feature Request only]
- type: textarea
id: proposed-solution
attributes:
label: Proposed solution [Feature Request only]
- type: textarea
id: alternatives-considered
attributes:
label: Alternatives considered [Feature Request only]
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@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Saucelabs Variables
uses: angular/dev-infra/github-actions/saucelabs@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/saucelabs@1ce250a660eca5f613794a32cbae6d0f7a449918
- 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@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@1ce250a660eca5f613794a32cbae6d0f7a449918
- 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@1ce250a660eca5f613794a32cbae6d0f7a449918
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@1ce250a660eca5f613794a32cbae6d0f7a449918
with:
github-token: '${{secrets.GITHUB_TOKEN}}'
workflow-artifact-name: 'adev-preview'
@@ -14,9 +14,9 @@ jobs:
runs-on: ubuntu-latest
if: github.event.repository.fork == false
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- uses: angular/dev-infra/github-actions/branch-manager@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
- uses: angular/dev-infra/github-actions/branch-manager@1ce250a660eca5f613794a32cbae6d0f7a449918
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+2 -2
View File
@@ -28,7 +28,7 @@ jobs:
- uses: alessbell/pull-request-comment-branch@aad01d65d6982b8eacabed5e9a684cd8ceb98da6 # v1.1
id: comment-branch
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# Specify repository as the PR branch might be from a fork.
repository: ${{steps.comment-branch.outputs.head_owner}}/${{steps.comment-branch.outputs.head_repo}}
@@ -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@1ce250a660eca5f613794a32cbae6d0f7a449918
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@1ce250a660eca5f613794a32cbae6d0f7a449918
- 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@1ce250a660eca5f613794a32cbae6d0f7a449918
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@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@1ce250a660eca5f613794a32cbae6d0f7a449918
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@2ad32e649e4db26c07674ebae31a297601dbcbaf # v6.10.8
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@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@1ce250a660eca5f613794a32cbae6d0f7a449918
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@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@1ce250a660eca5f613794a32cbae6d0f7a449918
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@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@1ce250a660eca5f613794a32cbae6d0f7a449918
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@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@1ce250a660eca5f613794a32cbae6d0f7a449918
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@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@1ce250a660eca5f613794a32cbae6d0f7a449918
- 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@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@1ce250a660eca5f613794a32cbae6d0f7a449918
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@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Build adev
+2 -2
View File
@@ -24,7 +24,7 @@ jobs:
steps:
- name: Checkout the repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
# Setting `persist-credentials: false` prevents the github-action account from being the
# account that is attempted to be used for authentication, instead the remote is set to
@@ -37,7 +37,7 @@ jobs:
ANGULAR_READONLY_GITHUB_TOKEN: ${{ secrets.READONLY_GITHUB_TOKEN }}
- name: Create a PR (if necessary)
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0
uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0
with:
token: ${{ secrets.ANGULAR_ROBOT_ACCESS_TOKEN }}
push-to-fork: 'angular-robot/angular'
+5 -18
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: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: angular/dev-infra/github-actions/pull-request-labeling@1ce250a660eca5f613794a32cbae6d0f7a449918
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: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: angular/dev-infra/github-actions/post-approval-changes@1ce250a660eca5f613794a32cbae6d0f7a449918
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 }}
+2 -2
View File
@@ -13,8 +13,8 @@ jobs:
statuses: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: angular/dev-infra/github-actions/google-internal-tests@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
- uses: angular/dev-infra/github-actions/google-internal-tests@1ce250a660eca5f613794a32cbae6d0f7a449918
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@1ce250a660eca5f613794a32cbae6d0f7a449918
- 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@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Saucelabs Variables
uses: angular/dev-infra/github-actions/saucelabs@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/saucelabs@1ce250a660eca5f613794a32cbae6d0f7a449918
- 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@1ce250a660eca5f613794a32cbae6d0f7a449918
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@1ce250a660eca5f613794a32cbae6d0f7a449918
- 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@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@1ce250a660eca5f613794a32cbae6d0f7a449918
- 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@1ce250a660eca5f613794a32cbae6d0f7a449918
- 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@1ce250a660eca5f613794a32cbae6d0f7a449918
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@1ce250a660eca5f613794a32cbae6d0f7a449918
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@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@1ce250a660eca5f613794a32cbae6d0f7a449918
- 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@2ad32e649e4db26c07674ebae31a297601dbcbaf # v6.10.8
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@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@1ce250a660eca5f613794a32cbae6d0f7a449918
- 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@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel Remote Caching
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@1ce250a660eca5f613794a32cbae6d0f7a449918
- 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@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@1ce250a660eca5f613794a32cbae6d0f7a449918
- 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@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@1ce250a660eca5f613794a32cbae6d0f7a449918
- 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@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel
uses: angular/dev-infra/github-actions/bazel/setup@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/setup@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Setup Bazel RBE
uses: angular/dev-infra/github-actions/bazel/configure-remote@e4dcda13e469f50a6bd92667e2ded8b98173bf2c
uses: angular/dev-infra/github-actions/bazel/configure-remote@1ce250a660eca5f613794a32cbae6d0f7a449918
- name: Install node modules
run: pnpm install --frozen-lockfile
- run: |
+2 -2
View File
@@ -25,7 +25,7 @@ jobs:
steps:
- name: 'Checkout code'
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
@@ -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@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v4.31.9
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
-34
View File
@@ -1,34 +0,0 @@
---
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.
## Key Documentation
- [Building and Testing](contributing-docs/building-and-testing-angular.md): definitive guide for running targets.
- [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.
-211
View File
@@ -1,200 +1,3 @@
<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
| Commit | Type | Description |
| -- | -- | -- |
| [2b254bc050](https://github.com/angular/angular/commit/2b254bc0508b73aab8991c3b1a9a703c339cb735) | fix | `linkedSignal.update` should propagate errors |
| [e5110b4fa1](https://github.com/angular/angular/commit/e5110b4fa155e4669ed507f3460d2d29026a28ab) | fix | export DirectiveWithBindings |
| [2cf4da0ea1](https://github.com/angular/angular/commit/2cf4da0ea11f5746eb7ae4dfd775f757576e4d98) | fix | hold constructors weakly in DepsTracker cache |
| [70a5b651be](https://github.com/angular/angular/commit/70a5b651be29f1421eb25150b560bfe154aad6bc) | fix | prevent element duplication with dynamic components |
### forms
| Commit | Type | Description |
| -- | -- | -- |
| [6f75b6e3f6](https://github.com/angular/angular/commit/6f75b6e3f60dc2a4f33e13562649931dc95eb52b) | fix | Resolves debounce promise on abort in debounceForDuration |
### localize
| Commit | Type | Description |
| -- | -- | -- |
| [4c7126d23b](https://github.com/angular/angular/commit/4c7126d23be3e43b1d5bd6f2fb13119d185c3682) | fix | add support for unit-test builder in ng-add schematic |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [d6268c0bbb](https://github.com/angular/angular/commit/d6268c0bbbdc92abaaaeb8eebee3bc45decab9c9) | fix | limit UrlParser recursion depth to prevent stack overflow |
| [49a36f4cc7](https://github.com/angular/angular/commit/49a36f4cc7254420bc34fff4e0f0242e00970280) | perf | Use .bind to avoid holding other closures in memory |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.1.2"></a>
# 21.1.2 (2026-01-28)
### forms
| Commit | Type | Description |
| -- | -- | -- |
| [9f99b14882](https://github.com/angular/angular/commit/9f99b14882bc4f883aa33295856010a8bca900fa) | fix | only touch visible, interactive fields on submit |
### language-service
| Commit | Type | Description |
| -- | -- | -- |
| [c57b0355b5](https://github.com/angular/angular/commit/c57b0355b51f5aee5abd822f203fc3bcc3e85acd) | fix | Detect local project version on creation |
### router
| Commit | Type | Description |
| -- | -- | -- |
| [21ecdc036a](https://github.com/angular/angular/commit/21ecdc036a46c487d6c5b6bd25c2bbc3e53a60f9) | fix | Do not intercept reload events with Navigation integration |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.1.1"></a>
# 21.1.1 (2026-01-21)
### compiler-cli
| Commit | Type | Description |
| -- | -- | -- |
| [0e1f1ed573](https://github.com/angular/angular/commit/0e1f1ed5732f3bb4d5dfbd1f0ee5a5be840594e4) | fix | drop .tsx extension for generated relative imports |
### core
| Commit | Type | Description |
| -- | -- | -- |
| [05adfcf8f2](https://github.com/angular/angular/commit/05adfcf8f26013ac20c38f2b08847b5142e4fd85) | fix | handle Set in class bindings |
### forms
| Commit | Type | Description |
| -- | -- | -- |
| [d89a80a970](https://github.com/angular/angular/commit/d89a80a970d9590df0509d8b94090904a99b7aca) | feat | Ability to manually register a form field binding in signal forms |
| [cb75f9ce85](https://github.com/angular/angular/commit/cb75f9ce85160b2e4359610c06294929ac1169c0) | fix | fix control value syncing on touch |
<!-- CHANGELOG SPLIT MARKER -->
<a name="21.1.0"></a>
# 21.1.0 (2026-01-14)
## Deprecations
@@ -245,20 +48,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
+24 -23
View File
@@ -5,27 +5,27 @@ module(
)
bazel_dep(name = "rules_pkg", version = "1.2.0")
bazel_dep(name = "rules_nodejs", version = "6.7.3")
bazel_dep(name = "aspect_rules_ts", version = "3.8.5")
bazel_dep(name = "aspect_rules_js", version = "2.9.2")
bazel_dep(name = "rules_nodejs", version = "6.6.2")
bazel_dep(name = "aspect_rules_ts", version = "3.8.1")
bazel_dep(name = "aspect_rules_js", version = "2.8.3")
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 = "aspect_bazel_lib", version = "2.22.2")
bazel_dep(name = "tar.bzl", version = "0.7.0")
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 = "7133b97252508f8528e5c5818a9a73cacc2e2a0e",
remote = "https://github.com/devversion/rules_angular.git",
)
bazel_dep(name = "devinfra")
git_override(
module_name = "devinfra",
commit = "e4dcda13e469f50a6bd92667e2ded8b98173bf2c",
commit = "1ce250a660eca5f613794a32cbae6d0f7a449918",
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.30.3",
pnpm_version_integrity = "sha512-yWHR4KLY41TsqlFmuCJRZmi39Ey1vZUSLVkN2Bki9gb1RzttI+xKW+Bef80Y6EiNR9l4u+mBhy8RRdBumnQAFw==",
pnpm_version = "10.28.0",
pnpm_version_integrity = "sha512-Bd9x0UIfITmeBT/eVnzqNNRG+gLHZXFEG/wceVbpjjYwiJgtlARl/TRIDU2QoGaLwSNi+KqIAApk6D0LDke+SA==",
)
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",
+297 -303
View File
@@ -13,28 +13,28 @@
"https://bcr.bazel.build/modules/aspect_bazel_lib/2.0.0/MODULE.bazel": "e118477db5c49419a88d78ebc7a2c2cea9d49600fe0f490c1903324a2c16ecd9",
"https://bcr.bazel.build/modules/aspect_bazel_lib/2.11.0/MODULE.bazel": "cb1ba9f9999ed0bc08600c221f532c1ddd8d217686b32ba7d45b0713b5131452",
"https://bcr.bazel.build/modules/aspect_bazel_lib/2.14.0/MODULE.bazel": "2b31ffcc9bdc8295b2167e07a757dbbc9ac8906e7028e5170a3708cecaac119f",
"https://bcr.bazel.build/modules/aspect_bazel_lib/2.19.2/MODULE.bazel": "30dfabbfae0139b1f0036e01c201dd4c0167da3017f0b7ef3820d78e07622989",
"https://bcr.bazel.build/modules/aspect_bazel_lib/2.17.1/MODULE.bazel": "9b027af55f619c7c444cead71061578fab6587e5e1303fa4ed61d49d2b1a7262",
"https://bcr.bazel.build/modules/aspect_bazel_lib/2.19.3/MODULE.bazel": "253d739ba126f62a5767d832765b12b59e9f8d2bc88cc1572f4a73e46eb298ca",
"https://bcr.bazel.build/modules/aspect_bazel_lib/2.22.5/MODULE.bazel": "004ba890363d05372a97248c37205ae64b6fa31047629cd2c0895a9d0c7779e8",
"https://bcr.bazel.build/modules/aspect_bazel_lib/2.22.5/source.json": "ac2c3213df8f985785f1d0aeb7f0f73d5324e6e67d593d9b9470fb74a25d4a9b",
"https://bcr.bazel.build/modules/aspect_bazel_lib/2.22.2/MODULE.bazel": "6b735f3fdd64978e217c9725f4ff0d84bf606554c8e77d20e90977841d7ff2ed",
"https://bcr.bazel.build/modules/aspect_bazel_lib/2.22.2/source.json": "58fffa2d722cff47cb8d921c8bbed7701c53f233009d9ca82beb4a0fb8fb9418",
"https://bcr.bazel.build/modules/aspect_bazel_lib/2.7.7/MODULE.bazel": "491f8681205e31bb57892d67442ce448cda4f472a8e6b3dc062865e29a64f89c",
"https://bcr.bazel.build/modules/aspect_bazel_lib/2.8.1/MODULE.bazel": "812d2dd42f65dca362152101fbec418029cc8fd34cbad1a2fde905383d705838",
"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",
"https://bcr.bazel.build/modules/aspect_rules_js/2.9.2/MODULE.bazel": "93fd5b85e6e912fb0712cbab453c43271d4ea33a093f84fd587638fbc9f8c145",
"https://bcr.bazel.build/modules/aspect_rules_js/2.9.2/source.json": "4bff7c03ab387b60deb15649ba575688e62f2a71a7544cbc7a660b19ec473808",
"https://bcr.bazel.build/modules/aspect_rules_js/2.8.3/MODULE.bazel": "807ce5f624124a8bc586c743394d174e85f0f9c6c4e0e2410b4088aebe790ac8",
"https://bcr.bazel.build/modules/aspect_rules_js/2.8.3/source.json": "c35cb4e04f61a09c17f8c569894b80de884b1e3dee2d33829704786e3f778037",
"https://bcr.bazel.build/modules/aspect_rules_rollup/2.0.1/MODULE.bazel": "296e3a053658c2af989ba9bd62a205e6d1fa84bdd6dd5249196546e6b84770ec",
"https://bcr.bazel.build/modules/aspect_rules_rollup/2.0.1/source.json": "2fe8ac1ccb4de74bf884761e070010280b272d94e3997205b361b91c75409726",
"https://bcr.bazel.build/modules/aspect_rules_ts/3.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.1/MODULE.bazel": "796622c65ae3008374fc2d77c32ddb4ef6da9fe891826ce648f70033a48b3667",
"https://bcr.bazel.build/modules/aspect_rules_ts/3.8.1/source.json": "a7c4f332f5c21f4e63d073f8dda40bf278d5307499fb307b35058dba558f417a",
"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",
@@ -47,11 +47,9 @@
"https://bcr.bazel.build/modules/bazel_features/1.18.0/MODULE.bazel": "1be0ae2557ab3a72a57aeb31b29be347bcdc5d2b1eb1e70f39e3851a7e97041a",
"https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58",
"https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b",
"https://bcr.bazel.build/modules/bazel_features/1.28.0/MODULE.bazel": "4b4200e6cbf8fa335b2c3f43e1d6ef3e240319c33d43d60cc0fbd4b87ece299d",
"https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87",
"https://bcr.bazel.build/modules/bazel_features/1.34.0/MODULE.bazel": "e8475ad7c8965542e0c7aac8af68eb48c4af904be3d614b6aa6274c092c2ea1e",
"https://bcr.bazel.build/modules/bazel_features/1.39.0/MODULE.bazel": "28739425c1fc283c91931619749c832b555e60bcd1010b40d8441ce0a5cf726d",
"https://bcr.bazel.build/modules/bazel_features/1.39.0/source.json": "f63cbeb4c602098484d57001e5a07d31cb02bbccde9b5e2c9bf0b29d05283e93",
"https://bcr.bazel.build/modules/bazel_features/1.34.0/source.json": "dfa5c4b01110313153b484a735764d247fee5624bbab63d25289e43b151a657a",
"https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7",
"https://bcr.bazel.build/modules/bazel_features/1.9.0/MODULE.bazel": "885151d58d90d8d9c811eb75e3288c11f850e1d6b481a8c9f766adee4712358b",
"https://bcr.bazel.build/modules/bazel_features/1.9.1/MODULE.bazel": "8f679097876a9b609ad1f60249c49d68bfab783dd9be012faf9d82547b14815a",
@@ -77,8 +75,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",
@@ -125,9 +122,7 @@
"https://bcr.bazel.build/modules/rules_cc/0.0.8/MODULE.bazel": "964c85c82cfeb6f3855e6a07054fdb159aced38e99a5eecf7bce9d53990afa3e",
"https://bcr.bazel.build/modules/rules_cc/0.0.9/MODULE.bazel": "836e76439f354b89afe6a911a7adf59a6b2518fafb174483ad78a2a2fde7b1c5",
"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_cc/0.1.1/source.json": "d61627377bd7dd1da4652063e368d9366fc9a73920bfa396798ad92172cf645c",
"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",
@@ -162,11 +157,10 @@
"https://bcr.bazel.build/modules/rules_license/1.0.0/source.json": "a52c89e54cc311196e478f8382df91c15f7a2bfdf4c6cd0e2675cc2ff0b56efb",
"https://bcr.bazel.build/modules/rules_nodejs/6.2.0/MODULE.bazel": "ec27907f55eb34705adb4e8257952162a2d4c3ed0f0b3b4c3c1aad1fac7be35e",
"https://bcr.bazel.build/modules/rules_nodejs/6.3.0/MODULE.bazel": "45345e4aba35dd6e4701c1eebf5a4e67af4ed708def9ebcdc6027585b34ee52d",
"https://bcr.bazel.build/modules/rules_nodejs/6.3.3/MODULE.bazel": "b66eadebd10f1f1b25f52f95ab5213a57e82c37c3f656fcd9a57ad04d2264ce7",
"https://bcr.bazel.build/modules/rules_nodejs/6.5.0/MODULE.bazel": "546d0cf79f36f9f6e080816045f97234b071c205f4542e3351bd4424282a8810",
"https://bcr.bazel.build/modules/rules_nodejs/6.5.2/MODULE.bazel": "7f9ea68a0ce6d82905ce9f74e76ab8a8b4531ed4c747018c9d76424ad0b3370d",
"https://bcr.bazel.build/modules/rules_nodejs/6.7.3/MODULE.bazel": "c22a48b2a0dbf05a9dc5f83837bbc24c226c1f6e618de3c3a610044c9f336056",
"https://bcr.bazel.build/modules/rules_nodejs/6.7.3/source.json": "a3f966f4415a8a6545e560ee5449eac95cc633f96429d08e87c87775c72f5e09",
"https://bcr.bazel.build/modules/rules_nodejs/6.6.2/MODULE.bazel": "9fdb5e1d50246a25761f150fcc820dc47e4052330a8408451e628804f9ca64a6",
"https://bcr.bazel.build/modules/rules_nodejs/6.6.2/source.json": "6e8c1ecc64ff8da147c1620f862ad77d7b19c5d1b52b3aa5e847d5b3d0de4cc3",
"https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc",
"https://bcr.bazel.build/modules/rules_pkg/1.0.1/MODULE.bazel": "5b1df97dbc29623bccdf2b0dcd0f5cb08e2f2c9050aab1092fd39a41e82686ff",
"https://bcr.bazel.build/modules/rules_pkg/1.2.0/MODULE.bazel": "c7db3c2b407e673c7a39e3625dc05dc9f12d6682cbd82a3a5924a13b491eda7e",
@@ -200,14 +194,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.7.0/MODULE.bazel": "cc1acd85da33c80e430b65219a620d54d114628df24a618c3a5fa0b65e988da9",
"https://bcr.bazel.build/modules/tar.bzl/0.7.0/source.json": "9becb80306f42d4810bfa16379fb48aad0b01ce5342bc12fe47dcd6af3ac4d7a",
"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 +211,7 @@
"moduleExtensions": {
"@@aspect_rules_esbuild+//esbuild:extensions.bzl%esbuild": {
"general": {
"bzlTransitiveDigest": "fzdwsWU1Px4bYL57Cw1rvU/mgOqB7f0AF51JxT5kAs8=",
"bzlTransitiveDigest": "9odiC0alKLq5PUYv/CZiw2yiMHsGpxvEhPsqt//fRRk=",
"usagesDigest": "ToTaCONCN/E05krnHXLM1kpV1zrHNxHrGpUip973II4=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
@@ -418,8 +412,8 @@
},
"@@aspect_rules_js+//npm:extensions.bzl%pnpm": {
"general": {
"bzlTransitiveDigest": "9Zmqluz9ltvCoc6pskOslTkQpkH6CIINjWVkgam9aa4=",
"usagesDigest": "k3HnPWCDfdjPprODR4rLNwRc8pEk4QMKpK9YbIwIkkk=",
"bzlTransitiveDigest": "VaGKuK2sKVTWlq9QBZfNc4fhigCeN9Wt4YhnhdGUkv8=",
"usagesDigest": "V3eYj3dhR+UR/L7u17ZGfCuqJZpobNrN/6oEZPkZgDc=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
@@ -428,11 +422,11 @@
"repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_rule",
"attributes": {
"package": "pnpm",
"version": "10.30.3",
"version": "10.28.0",
"root_package": "",
"link_workspace": "",
"link_packages": {},
"integrity": "sha512-yWHR4KLY41TsqlFmuCJRZmi39Ey1vZUSLVkN2Bki9gb1RzttI+xKW+Bef80Y6EiNR9l4u+mBhy8RRdBumnQAFw==",
"integrity": "sha512-Bd9x0UIfITmeBT/eVnzqNNRG+gLHZXFEG/wceVbpjjYwiJgtlARl/TRIDU2QoGaLwSNi+KqIAApk6D0LDke+SA==",
"url": "",
"commit": "",
"patch_args": [
@@ -455,7 +449,7 @@
"repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_links",
"attributes": {
"package": "pnpm",
"version": "10.30.3",
"version": "10.28.0",
"dev": false,
"root_package": "",
"link_packages": {},
@@ -572,10 +566,10 @@
},
"@@aspect_rules_ts+//ts:extensions.bzl%ext": {
"general": {
"bzlTransitiveDigest": "6NyLUdrb79stFKm8oWIBK2q65cT5Upbr8IR2Qwsywu8=",
"usagesDigest": "ZXlhH7TrNEERA0ibuRRxAjYv2fVuXoFGNd3v+0UK1/g=",
"bzlTransitiveDigest": "7k3bewVApw4Kc6Rpho1Rrs1nrW/5jphUA5Mh1iHE2U4=",
"usagesDigest": "BIPOUwqzuuYqLOw/0p3h5GwHIDeBVXvNcxNoG5YlsHQ=",
"recordedFileInputs": {
"@@rules_browsers+//package.json": "772d873d450a539e2133635aeb5e63744cf1cec86e6b37aeecd9267a147fb0d7"
"@@rules_browsers+//package.json": "84dc1ba9b1c667a25894e97218bd8f247d54f24bb694efb397a881be3c06a4c5"
},
"recordedDirentsInputs": {},
"envVariables": {},
@@ -639,7 +633,7 @@
"@@aspect_tools_telemetry+//:extension.bzl%telemetry": {
"general": {
"bzlTransitiveDigest": "cl5A2O84vDL6Tt+Qga8FCj1DUDGqn+e7ly5rZ+4xvcc=",
"usagesDigest": "fkarigrHiiNAt/BwClRuO51QOEo6226baK6LoK/IWdU=",
"usagesDigest": "c8kIEi+rX5/1OpfMZW0mHrM7sAvw+nu7pOep2xSjFb4=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
@@ -648,10 +642,10 @@
"repoRuleId": "@@aspect_tools_telemetry+//:extension.bzl%tel_repository",
"attributes": {
"deps": {
"aspect_rules_ts": "3.8.5",
"aspect_rules_js": "2.9.2",
"aspect_rules_ts": "3.8.1",
"aspect_rules_js": "2.8.3",
"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 +667,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 +727,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 +736,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 +754,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 +772,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 +790,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 +808,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 +824,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 +840,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 +856,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 +872,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 +888,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 +904,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 +920,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 +939,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 +1022,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": {},
@@ -1092,8 +1086,8 @@
},
"@@rules_nodejs+//nodejs:extensions.bzl%node": {
"general": {
"bzlTransitiveDigest": "4pUxCNc22K4I+6+4Nxu52Hur12tFRfa1JMsN5mdDv60=",
"usagesDigest": "pjXeuItYQffkhMcQv+aU/G0SB0QAmyOC9W7iq63eW08=",
"bzlTransitiveDigest": "NwcLXHrbh2hoorA/Ybmcpjxsn/6avQmewDglodkDrgo=",
"usagesDigest": "JCQTSBqneRVlPU6lK5eO3tfMlb29Zvh+cPUZ8rY2q8U=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
@@ -1103,46 +1097,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 +1146,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 +1195,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 +1244,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 +1293,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 +1342,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 +1391,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 +1440,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 +1508,7 @@
},
"@@rules_python+//python/extensions:pip.bzl%pip": {
"general": {
"bzlTransitiveDigest": "LFkevJ0u48WgTVCyU07LkRD8iYwsRUs2M7uSFHkQAW0=",
"bzlTransitiveDigest": "rPZpN8uoLnuu3B+LRiijfMadoMlO1/dxOO8/emPCZV4=",
"usagesDigest": "/9NP3RV6/DWuNdYAsIU/8UCgCX0TdPUJr0X6O+0lrtk=",
"recordedFileInputs": {
"@@protobuf+//python/requirements.txt": "983be60d3cec4b319dcab6d48aeb3f5b2f7c3350f26b3a9e97486c37967c73c5",
@@ -4308,8 +4302,8 @@
},
"@@yq.bzl+//yq:extensions.bzl%yq": {
"general": {
"bzlTransitiveDigest": "UfFMy8CWK4/dVo/tfaSAIYUiDGNAPes5eRllx9O9Q9Q=",
"usagesDigest": "LPHUB1lzZAsPKLeNh6HnzS3+5eXDr8A5CKmoD+VBOA4=",
"bzlTransitiveDigest": "tDqk+ntWTdxNAWPDjRY1uITgHbti2jcXR5ZdinltBs0=",
"usagesDigest": "fKeGl7m4X6ZwRQS4Up/5Bzx+9laXzZyKEn6s9ODJQTc=",
"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.0",
"@angular/build": "21.1.0-rc.0",
"@angular/cdk": "21.1.0",
"@angular/cli": "21.1.0-rc.0",
"@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.0",
"@angular/platform-browser": "workspace:*",
"@angular/platform-server": "workspace:*",
"@angular/router": "workspace:*",
"@angular/ssr": "21.2.0",
"@angular/ssr": "21.1.0-rc.0",
"@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.7.4",
"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
}
@@ -8,7 +8,7 @@
import {NgTemplateOutlet} from '@angular/common';
import {ChangeDetectionStrategy, Component, inject, input, output} from '@angular/core';
import {MatTooltip} from '@angular/material/tooltip';
import {MatTooltipModule} from '@angular/material/tooltip';
import {RouterLink, RouterLinkActive} from '@angular/router';
import {NavigationItem} from '../../interfaces/index';
import {IsActiveNavigationItem} from '../../pipes';
@@ -23,7 +23,7 @@ import {IconComponent} from '../icon/icon.component';
IconComponent,
IsActiveNavigationItem,
NgTemplateOutlet,
MatTooltip,
MatTooltipModule,
],
templateUrl: './navigation-list.component.html',
styleUrls: ['./navigation-list.component.scss'],
@@ -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();
},
});
@@ -15,7 +15,6 @@ ng_project(
"//adev/shared-docs/components:__pkg__",
],
deps = [
"//adev:node_modules/@angular/aria",
"//adev:node_modules/@angular/common",
"//adev:node_modules/@angular/core",
"//adev:node_modules/@angular/forms",
@@ -1,61 +1,10 @@
<div ngCombobox #outerCombobox="ngCombobox" [readonly]="true" [disabled]="disabled()">
<div class="docs-select-input-container">
<input
ngComboboxInput
[attr.id]="id()"
[attr.name]="name()"
[value]="displayValue()"
placeholder="Select an option..."
readonly
/>
<span class="material-symbols-outlined docs-select-arrow" translate="no" aria-hidden="true"
>arrow_drop_down</span
>
</div>
<ng-template ngComboboxPopupContainer>
<dialog ngComboboxDialog class="docs-select-dialog">
<div ngCombobox #innerCombobox="ngCombobox" filterMode="manual" [alwaysExpanded]="true">
<div class="docs-select-search-container">
<span
class="material-symbols-outlined docs-select-search-icon"
translate="no"
aria-hidden="true"
>search</span
>
<input
ngComboboxInput
class="docs-select-search-input"
placeholder="Search..."
[(value)]="searchString"
/>
</div>
<ng-template ngComboboxPopupContainer>
@if (filteredOptions().length === 0) {
<div class="docs-select-no-results">No results found</div>
}
<div ngListbox [(values)]="selectedValues" class="docs-select-listbox">
@for (option of filteredOptions(); track option.value) {
<div
ngOption
[value]="option.value"
[label]="option.label"
class="docs-select-option"
>
<span class="docs-select-option-label">{{ option.label }}</span>
<span
class="material-symbols-outlined docs-select-check-icon"
translate="no"
aria-hidden="true"
>check</span
>
</div>
}
</div>
</ng-template>
</div>
</dialog>
</ng-template>
</div>
<select
[attr.id]="id()"
[attr.name]="name()"
[value]="value()"
(change)="setOption($any($event.target).value)"
>
@for (item of options(); track item) {
<option [value]="item.value" [selected]="value() === item.value">{{ item.label }}</option>
}
</select>
@@ -1,186 +0,0 @@
:host {
--border-color: color-mix(in srgb, var(--full-contrast) 20%, var(--page-background));
}
[ngCombobox] {
position: relative;
width: 100%;
display: flex;
flex-direction: column;
border: 1px solid var(--border-color);
border-radius: 0.25rem;
}
.docs-select-input-container {
display: flex;
position: relative;
align-items: center;
border-radius: 0.25rem;
}
[ngComboboxInput] {
border-radius: 0.25rem;
}
[ngComboboxInput][readonly='true'] {
cursor: pointer;
padding: 0.7rem 1rem;
}
[ngCombobox]:focus-within [ngComboboxInput]:not(.docs-select-search-input) {
outline: 1.5px solid var(--vivid-pink);
box-shadow: 0 0 0 4px color-mix(in srgb, var(--vivid-pink) 25%, transparent);
}
.docs-select-arrow {
width: 24px;
height: 24px;
font-size: 20px;
display: grid;
place-items: center;
pointer-events: none;
padding: 0 0.5rem;
position: absolute;
right: 0;
opacity: 0.8;
transition: transform 0.2s ease;
}
[ngComboboxInput][aria-expanded='true'] + .docs-select-arrow {
transform: rotate(180deg);
}
[ngComboboxInput] {
width: 100%;
border: none;
outline: none;
font-size: 1rem;
padding: 0.7rem 1rem 0.7rem 2.5rem;
background-color: var(--septenary-contrast);
color: var(--primary-contrast);
}
.docs-select-dialog {
position: absolute;
left: auto;
right: auto;
top: auto;
bottom: auto;
padding: 0;
border: 1px solid var(--border-color);
border-radius: 0.25rem;
background-color: var(--septenary-contrast);
color: inherit;
.docs-select-input-container {
border-radius: 0;
}
[ngCombobox],
.docs-select-input-container {
border: none;
}
[ngComboboxInput] {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
[ngCombobox]:focus-within [ngComboboxInput] {
outline: none;
box-shadow: none;
}
.docs-select-search-input:focus {
outline: none;
box-shadow: none;
}
.docs-select-search-container {
border-bottom: 1px solid var(--border-color);
}
&::backdrop {
opacity: 0;
}
}
.docs-select-search-container {
position: relative;
display: flex;
align-items: center;
border-radius: 0;
}
.docs-select-search-icon {
width: 24px;
height: 24px;
font-size: 20px;
display: grid;
place-items: center;
pointer-events: none;
position: absolute;
padding: 0 0.5rem;
opacity: 0.8;
}
.docs-select-search-input {
width: 100%;
border: none;
outline: none;
font-size: 0.875rem;
padding: 0.5rem 0.75rem 0.5rem 2rem;
background-color: var(--septenary-contrast);
color: var(--primary-contrast);
}
.docs-select-no-results {
padding: 0.75rem;
text-align: center;
font-size: 0.875rem;
color: var(--primary-contrast);
opacity: 0.7;
}
.docs-select-listbox {
display: flex;
flex-direction: column;
max-height: 12rem;
overflow: auto;
padding: 0.25rem;
}
.docs-select-option {
display: flex;
cursor: pointer;
align-items: center;
padding: 0.5rem 0.75rem;
font-size: 0.875rem;
border-radius: 0.25rem;
&:hover {
background-color: color-mix(in srgb, var(--primary-contrast) 5%, transparent);
}
&[data-active='true'] {
outline-offset: -2px;
outline: 2px solid var(--vivid-pink);
}
&[aria-selected='true'] {
color: var(--vivid-pink);
background-color: color-mix(in srgb, var(--vivid-pink) 5%, transparent);
}
&:not([aria-selected='true']) .docs-select-check-icon {
display: none;
}
}
.docs-select-option-label {
flex: 1;
}
.docs-select-check-icon {
font-size: 0.9rem;
}
@@ -6,26 +6,8 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {
Combobox,
ComboboxDialog,
ComboboxInput,
ComboboxPopupContainer,
} from '@angular/aria/combobox';
import {Listbox, Option} from '@angular/aria/listbox';
import {
afterRenderEffect,
ChangeDetectionStrategy,
Component,
computed,
input,
model,
signal,
untracked,
viewChild,
} from '@angular/core';
import {ChangeDetectionStrategy, Component, input, model} from '@angular/core';
import {FormValueControl} from '@angular/forms/signals';
import {FormsModule} from '@angular/forms';
type SelectOptionValue = string | number | boolean;
@@ -37,17 +19,11 @@ export interface SelectOption {
@Component({
selector: 'docs-select',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
Combobox,
ComboboxDialog,
ComboboxInput,
ComboboxPopupContainer,
FormsModule,
Listbox,
Option,
],
templateUrl: './select.component.html',
styleUrls: ['./select.component.scss'],
host: {
class: 'docs-form-element',
},
})
export class Select implements FormValueControl<string | null> {
readonly value = model<string | null>(null);
@@ -57,66 +33,11 @@ export class Select implements FormValueControl<string | null> {
readonly options = input.required<SelectOption[]>();
readonly disabled = input(false);
readonly dialog = viewChild(ComboboxDialog);
readonly listbox = viewChild<Listbox<SelectOptionValue>>(Listbox);
readonly combobox = viewChild<Combobox<SelectOptionValue>>(Combobox);
readonly searchString = signal('');
readonly filteredOptions = computed(() => {
const search = this.searchString().toLowerCase();
if (!search) {
return this.options();
}
return this.options().filter((option) => option.label.toLowerCase().includes(search));
});
readonly displayValue = computed(() => {
const currentValue = this.value();
if (currentValue === null) {
return '';
}
const option = this.options().find((opt) => opt.value === currentValue);
return option ? option.label : '';
});
readonly selectedValues = signal<SelectOptionValue[]>([]);
constructor() {
afterRenderEffect(() => {
if (this.dialog() && this.combobox()?.expanded()) {
untracked(() => this.listbox()?.gotoFirst());
this.positionDialog();
}
});
afterRenderEffect(() => {
const selected = this.selectedValues();
if (selected.length > 0) {
untracked(() => this.dialog()?.close());
this.value.set(selected[0] as string);
this.searchString.set('');
}
});
afterRenderEffect(() => this.listbox()?.scrollActiveItemIntoView());
}
// TODO: Improve once CDK overlay is fixed https://github.com/angular/components/issues/32504
private positionDialog(): void {
const dialog = this.dialog();
const combobox = this.combobox();
if (!dialog || !combobox) {
setOption($event: SelectOptionValue): void {
if (this.disabled()) {
return;
}
const comboboxRect = combobox.inputElement()?.getBoundingClientRect();
const scrollY = window.scrollY;
if (comboboxRect) {
dialog.element.style.width = `${comboboxRect.width}px`;
dialog.element.style.top = `${comboboxRect.bottom + scrollY + 4}px`;
dialog.element.style.left = `${comboboxRect.left}px`;
}
this.value.set($event as string);
}
}
@@ -32,32 +32,4 @@ describe('TabGroup', () => {
expect(tabs[0].textContent.trim()).toBe('tab 1');
expect(tabpanels[0].textContent.trim()).toBe('panel 1');
});
it('should generate unique ids for tabs and tabpanels', async () => {
const testPanel1 = document.createElement('div');
testPanel1.textContent = 'panel 1';
const testPanel2 = document.createElement('div');
testPanel2.textContent = 'panel 2';
fixture.componentRef.setInput('tabs', [
{label: 'tab 1', panel: testPanel1},
{label: 'tab 2', panel: testPanel2},
]);
await fixture.whenStable();
const tabs = fixture.nativeElement.querySelectorAll('.docs-tab');
const tabpanels = fixture.nativeElement.querySelectorAll('.docs-tab-panel');
expect(tabs[0].id).not.toBe(tabs[1].id);
expect(tabpanels[0].id).not.toBe(tabpanels[1].id);
const fixture2 = TestBed.createComponent(TabGroup);
fixture2.componentRef.setInput('tabs', [
{label: 'tab 1', panel: testPanel1},
{label: 'tab 2', panel: testPanel2},
]);
await fixture2.whenStable();
const tabs2 = fixture2.nativeElement.querySelectorAll('.docs-tab');
const tabpanels2 = fixture2.nativeElement.querySelectorAll('.docs-tab-panel');
expect(tabs[0].id).not.toBe(tabs2[0].id);
expect(tabpanels[0].id).not.toBe(tabpanels2[0].id);
});
});
@@ -7,19 +7,18 @@
*/
import {
afterRenderEffect,
ChangeDetectionStrategy,
Component,
computed,
ElementRef,
inject,
input,
inject,
computed,
linkedSignal,
Renderer2,
viewChildren,
afterRenderEffect,
Renderer2,
ElementRef,
ChangeDetectionStrategy,
} from '@angular/core';
let idCounter = 0;
import {_IdGenerator} from '@angular/cdk/a11y';
/**
* A simple tabs implementation with proper aria roles.
@@ -37,24 +36,20 @@ let idCounter = 0;
})
export class TabGroup {
private readonly _renderer = inject(Renderer2);
private readonly _idGenerator = inject(_IdGenerator);
private readonly _tabpanels = viewChildren<ElementRef<HTMLDivElement>>('tabpanel');
readonly tabs = input<{label: string; panel: HTMLElement}[]>();
readonly computedTabs = computed(() => {
return (
this.tabs()?.map((tab) => {
const id = idCounter++;
return {
tabId: `docs-tab-${id}`,
tabPanelId: `docs-tab-panel-${id}`,
label: tab.label,
panel: tab.panel,
};
}) ?? []
);
});
readonly computedTabs = computed(
() =>
this.tabs()?.map((tab) => ({
tabId: this._idGenerator.getId('docs-tab-'),
tabPanelId: this._idGenerator.getId('docs-tab-panel-'),
label: tab.label,
panel: tab.panel,
})) ?? [],
);
readonly selectedTab = linkedSignal(() => this.computedTabs()[0]?.tabId);
@@ -8,19 +8,11 @@
top: 0;
height: fit-content;
width: 14rem;
padding-right: 1rem;
padding-right: 2rem;
max-height: 100vh;
overflow-y: scroll;
box-sizing: border-box;
@include mq.for-large-desktop-up {
width: 16rem;
}
@include mq.for-extra-large-desktop-up {
width: 25rem;
}
aside {
margin-bottom: 2rem;
}
@@ -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,6 +9,7 @@
import {
afterNextRender,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
computed,
ElementRef,
@@ -19,13 +20,13 @@ import {
Type,
} from '@angular/core';
import {DOCUMENT, NgComponentOutlet, NgTemplateOutlet} from '@angular/common';
import {MatTab, MatTabGroup} from '@angular/material/tabs';
import {MatTabsModule} from '@angular/material/tabs';
import {Clipboard} from '@angular/cdk/clipboard';
import {CopySourceCodeButton} from '../../copy-source-code-button/copy-source-code-button.component';
import {IconComponent} from '../../icon/icon.component';
import {ExampleMetadata, Snippet} from '../../../interfaces/index';
import {EXAMPLE_VIEWER_CONTENT_LOADER} from '../../../providers/index';
import {MatTooltip} from '@angular/material/tooltip';
import {MatTooltipModule} from '@angular/material/tooltip';
export const CODE_LINE_NUMBER_CLASS_NAME = 'shiki-ln-number';
export const CODE_LINE_CLASS_NAME = 'line';
@@ -36,9 +37,8 @@ export const HIDDEN_CLASS_NAME = 'hidden';
selector: 'docs-example-viewer',
imports: [
CopySourceCodeButton,
MatTabGroup,
MatTab,
MatTooltip,
MatTabsModule,
MatTooltipModule,
IconComponent,
NgTemplateOutlet,
NgComponentOutlet,
+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",
-2
View File
@@ -47,7 +47,6 @@ js_binary(
"//adev:node_modules/jsdom",
"//adev:node_modules/mermaid",
"//adev:node_modules/playwright-core",
"//adev:node_modules/typescript",
"@rules_browsers//browsers/chromium",
],
entry_point = "//adev/shared-docs/pipeline/guides:guides.mjs",
@@ -62,7 +61,6 @@ js_binary(
name = "markdown_no_mermaid",
data = [
"//adev:node_modules/jsdom",
"//adev:node_modules/typescript",
],
entry_point = "//adev/shared-docs/pipeline/guides:guides-no-mermaid.mjs",
visibility = ["//visibility:public"],
@@ -7,7 +7,6 @@ esbuild(
entry_point = ":index.mts",
external = [
"@angular/compiler-cli",
"typescript",
],
format = "esm",
output = "bin.mjs",
@@ -8,7 +8,6 @@ esbuild(
external = [
"jsdom",
"playwright-core",
"typescript",
],
format = "esm",
output = "bin.mjs",
@@ -167,9 +167,11 @@ export type CliCommandRenderable = CliCommand & {
htmlDescription: string;
cards: CliCardRenderable[];
argumentsLabel: string;
optionsLabel: string;
hasOptions: boolean;
subcommands?: CliCommandRenderable[];
};
export interface InitializerApiFunctionRenderable
extends Omit<InitializerApiFunctionEntry, 'jsdocTags'>, DocEntryRenderable, HasRenderableToc {}
extends Omit<InitializerApiFunctionEntry, 'jsdocTags'>,
DocEntryRenderable,
HasRenderableToc {}
@@ -22,10 +22,6 @@ export interface HasJsDocTags {
jsdocTags: JsDocTagEntry[];
}
export interface MaybeJsDocTags {
jsdocTags?: JsDocTagEntry[];
}
export interface HasAdditionalLinks {
additionalLinks: LinkEntryRenderable[];
}
@@ -28,7 +28,6 @@ import {RawHtml} from './raw-html';
import {DeprecationWarning} from './deprecation-warning';
import {codeToHtml} from '../../../shared/shiki.mjs';
import {getHighlighterInstance} from '../shiki/shiki.mjs';
import {getSymbolsAsApiEntries} from '../symbol-context.mjs';
/** Component to render a class API reference document. */
export function ClassReference(
@@ -44,7 +43,6 @@ export function ClassReference(
<RawHtml
value={codeToHtml(getHighlighterInstance(), (entry as PipeEntry).usage, {
language: 'angular-html',
apiEntries: getSymbolsAsApiEntries(),
})}
/>
</div>
@@ -26,15 +26,19 @@ export function CliCommandReference(entry: CliCommandRenderable) {
<code>
<div className={'shiki line cli'}>
ng {commandName(entry, command)}
{entry.argumentsLabel && (
{entry.argumentsLabel ? (
<button member-id={'Arguments'} className="shiki-ln-line-argument">
{entry.argumentsLabel}
</button>
) : (
<></>
)}
{entry.optionsLabel && (
{entry.hasOptions ? (
<button member-id={'Options'} className="shiki-ln-line-option">
{entry.optionsLabel}
[options]
</button>
) : (
<></>
)}
</div>
</code>
@@ -73,14 +73,13 @@ export function FunctionReference(entry: FunctionEntryRenderable) {
<DeprecationWarning entry={entry} />
<SectionApi entry={entry} />
<div className={REFERENCE_MEMBERS}>
{entry.signatures.length > 1 &&
entry.signatures.map((s, i) =>
signatureCard(s.name, getFunctionMetadataRenderable(s, entry.moduleName, entry.repo), {
id: `${s.name}_${i}`,
printSignaturesAsHeader,
hideUsageNotes: true,
}),
)}
{entry.signatures.map((s, i) =>
signatureCard(s.name, getFunctionMetadataRenderable(s, entry.moduleName, entry.repo), {
id: `${s.name}_${i}`,
printSignaturesAsHeader,
hideUsageNotes: true,
}),
)}
</div>
<SectionDescription entry={entry} />
@@ -10,14 +10,10 @@ import {h} from 'preact';
import {RawHtml} from './raw-html';
import {codeToHtml} from '../../../shared/shiki.mjs';
import {getHighlighterInstance} from '../shiki/shiki.mjs';
import {getSymbolsAsApiEntries} from '../symbol-context.mjs';
/** Component to render a header of the CLI page. */
export function HighlightTypeScript(props: {code: string}) {
const result = codeToHtml(getHighlighterInstance(), props.code, {
language: 'typescript',
apiEntries: getSymbolsAsApiEntries(),
});
const result = codeToHtml(getHighlighterInstance(), props.code, {language: 'typescript'});
const withScrollTrack = result.replace(/^(<pre class="shiki)/, '$1 docs-mini-scroll-track');
return <RawHtml value={withScrollTrack} className="docs-code" />;
@@ -40,7 +40,7 @@ describe('CLI docs to html', () => {
const cliTocs = fragment.querySelectorAll('.docs-reference-cli-toc')!;
expect(cliTocs.length).toBe(2);
expect(cliTocs[0].textContent).toContain('ng component [name] [options]');
expect(cliTocs[1].textContent).toContain('ng c [name] [options]');
expect(cliTocs[0].textContent).toContain('ng component[name][options]');
expect(cliTocs[1].textContent).toContain('ng c[name][options]');
});
});
@@ -8,12 +8,12 @@
import {readFile} from 'fs/promises';
import {JSDOM} from 'jsdom';
import {resolve} from 'path';
import {initHighlighter} from '../../../shared/shiki.mjs';
import {getRenderable} from '../processing.mjs';
import {renderEntry} from '../rendering.mjs';
import {setHighlighterInstance} from '../shiki/shiki.mjs';
import {setSymbols} from '../symbol-context.mjs';
import {resolve} from 'path';
import {initHighlighter} from '../../../shared/shiki.mjs';
import {setHighlighterInstance} from '../shiki/shiki.mjs';
// Note: The tests will probably break if the schema of the api extraction changes.
// All entries in the fake-entries are extracted from Angular's api.
@@ -69,7 +69,7 @@ describe('markdown to html', () => {
it('should render multiple {@link} blocks', () => {
const provideClientHydrationEntry = entries.get('provideClientHydration')!;
expect(provideClientHydrationEntry).toBeDefined();
const cardItem = provideClientHydrationEntry.querySelector('.docs-api')!;
const cardItem = provideClientHydrationEntry.querySelector('.docs-reference-card-item')!;
expect(cardItem.innerHTML).not.toContain('@link');
});
@@ -17,207 +17,204 @@ import {
// @ts-ignore This compiles fine, but Webstorm doesn't like the ESM import in a CJS context.
describe('jsdoc transforms', () => {
describe('addHtmlAdditionalLinks', () => {
it('should transform links', () => {
setCurrentSymbol('Router');
setSymbols(
Object.fromEntries([
['Route', 'test'],
['Router', 'test'],
['Router.someMethod', 'test'],
['Router.someMethodWithParenthesis', 'test'],
['FormGroup', 'test'],
['FormGroup.someMethod', 'test'],
]),
);
it('should transform links', () => {
setCurrentSymbol('Router');
setSymbols(
Object.fromEntries([
['Route', 'test'],
['Router', 'test'],
['Router.someMethod', 'test'],
['Router.someMethodWithParenthesis', 'test'],
['FormGroup', 'test'],
['FormGroup.someMethod', 'test'],
]),
);
const entry = addHtmlAdditionalLinks({
jsdocTags: [
{
name: 'see',
comment: '[Angular](https://angular.io)',
},
{
name: 'see',
comment: '[Angular](https://angular.io "Angular")',
},
{
name: 'see',
comment: '{@link Route}',
},
{
name: 'see',
comment: '{@link Route Something else}',
},
{
name: 'see',
comment: '{@link #someMethod}',
},
{
name: 'see',
comment: '{@link #someMethodWithParenthesis()}',
},
{
name: 'see',
comment: '{@link someMethod()}',
},
{
name: 'see',
comment: '{@link FormGroup.someMethod()}',
},
{
name: 'see',
comment: '{@link https://angular.dev/api/core/ApplicationRef}',
},
{
name: 'see',
comment: '{@link https://angular.dev}',
},
{
name: 'see',
comment: '{@link /cli/build ng build}',
},
{
name: 'see',
comment: '{@link /ecosystem/rxjs-interop/output-interop Output Interop}',
},
],
moduleName: 'test',
});
expect(entry.additionalLinks[0]).toEqual({
label: 'Angular',
url: 'https://angular.io',
title: undefined,
});
expect(entry.additionalLinks[1]).toEqual({
label: 'Angular',
url: 'https://angular.io',
title: 'Angular',
});
expect(entry.additionalLinks[2]).toEqual({
label: 'Route',
url: '/api/test/Route',
});
expect(entry.additionalLinks[3]).toEqual({
label: 'Something else',
url: '/api/test/Route',
});
expect(entry.additionalLinks[4]).toEqual({
label: 'someMethod',
url: '/api/test/Router#someMethod',
});
expect(entry.additionalLinks[5]).toEqual({
label: 'someMethodWithParenthesis()',
url: '/api/test/Router#someMethodWithParenthesis',
});
expect(entry.additionalLinks[6]).toEqual({
label: 'someMethod()',
url: '/api/test/Router#someMethod',
});
expect(entry.additionalLinks[7]).toEqual({
label: 'FormGroup.someMethod()',
url: '/api/test/FormGroup#someMethod',
});
expect(entry.additionalLinks[8]).toEqual({
label: 'ApplicationRef',
url: 'https://angular.dev/api/core/ApplicationRef',
});
expect(entry.additionalLinks[9]).toEqual({
label: 'angular.dev',
url: 'https://angular.dev',
});
expect(entry.additionalLinks[10]).toEqual({
label: 'ng build',
url: '/cli/build',
});
expect(entry.additionalLinks[11]).toEqual({
label: 'Output Interop',
url: '/ecosystem/rxjs-interop/output-interop',
});
const entry = addHtmlAdditionalLinks({
jsdocTags: [
{
name: 'see',
comment: '[Angular](https://angular.io)',
},
{
name: 'see',
comment: '[Angular](https://angular.io "Angular")',
},
{
name: 'see',
comment: '{@link Route}',
},
{
name: 'see',
comment: '{@link Route Something else}',
},
{
name: 'see',
comment: '{@link #someMethod}',
},
{
name: 'see',
comment: '{@link #someMethodWithParenthesis()}',
},
{
name: 'see',
comment: '{@link someMethod()}',
},
{
name: 'see',
comment: '{@link FormGroup.someMethod()}',
},
{
name: 'see',
comment: '{@link https://angular.dev/api/core/ApplicationRef}',
},
{
name: 'see',
comment: '{@link https://angular.dev}',
},
{
name: 'see',
comment: '{@link /cli/build ng build}',
},
{
name: 'see',
comment: '{@link /ecosystem/rxjs-interop/output-interop Output Interop}',
},
],
moduleName: 'test',
});
it('should convert backticks to code tags in markdown links', () => {
const entry = addHtmlAdditionalLinks({
jsdocTags: [
{
name: 'see',
comment:
'[Host view using `ViewContainerRef.createComponent`](guide/components/programmatic-rendering#host-view-using-viewcontainerrefcreatecomponent)',
},
{
name: 'see',
comment:
'[Popup attached to `document.body` with `createComponent` + `hostElement`](guide/components/programmatic-rendering#popup-attached-to-documentbody-with-createcomponent--hostelement)',
},
{
name: 'see',
comment: '[Method with `backticks` in title](https://example.com "Title with `code`")',
},
],
moduleName: 'test',
});
expect(entry.additionalLinks[0]).toEqual({
label: 'Host view using <code>ViewContainerRef.createComponent</code>',
url: 'guide/components/programmatic-rendering#host-view-using-viewcontainerrefcreatecomponent',
title: undefined,
});
expect(entry.additionalLinks[1]).toEqual({
label:
'Popup attached to <code>document.body</code> with <code>createComponent</code> + <code>hostElement</code>',
url: 'guide/components/programmatic-rendering#popup-attached-to-documentbody-with-createcomponent--hostelement',
title: undefined,
});
expect(entry.additionalLinks[2]).toEqual({
label: 'Method with <code>backticks</code> in title',
url: 'https://example.com',
title: 'Title with `code`',
});
expect(entry.additionalLinks[0]).toEqual({
label: 'Angular',
url: 'https://angular.io',
title: undefined,
});
it('should throw on invalid relatie @link', () => {
const entryFn = () =>
addHtmlAdditionalLinks({
jsdocTags: [
{
name: 'see',
comment: '{@link cli/build ng build}',
},
],
moduleName: 'test',
});
expect(entry.additionalLinks[1]).toEqual({
label: 'Angular',
url: 'https://angular.io',
title: 'Angular',
});
expect(entryFn).toThrowError(/Forbidden relative link: cli\/build ng build/);
expect(entry.additionalLinks[2]).toEqual({
label: 'Route',
url: '/api/test/Route',
});
expect(entry.additionalLinks[3]).toEqual({
label: 'Something else',
url: '/api/test/Route',
});
expect(entry.additionalLinks[4]).toEqual({
label: 'someMethod',
url: '/api/test/Router#someMethod',
});
expect(entry.additionalLinks[5]).toEqual({
label: 'someMethodWithParenthesis()',
url: '/api/test/Router#someMethodWithParenthesis',
});
expect(entry.additionalLinks[6]).toEqual({
label: 'someMethod()',
url: '/api/test/Router#someMethod',
});
expect(entry.additionalLinks[7]).toEqual({
label: 'FormGroup.someMethod()',
url: '/api/test/FormGroup#someMethod',
});
expect(entry.additionalLinks[8]).toEqual({
label: 'ApplicationRef',
url: 'https://angular.dev/api/core/ApplicationRef',
});
expect(entry.additionalLinks[9]).toEqual({
label: 'angular.dev',
url: 'https://angular.dev',
});
expect(entry.additionalLinks[10]).toEqual({
label: 'ng build',
url: '/cli/build',
});
expect(entry.additionalLinks[11]).toEqual({
label: 'Output Interop',
url: '/ecosystem/rxjs-interop/output-interop',
});
});
describe('addHtmlDescription', () => {
it('should parse markdown in descriptions', async () => {
setHighlighterInstance(await initHighlighter());
it('should convert backticks to code tags in markdown links', () => {
const entry = addHtmlAdditionalLinks({
jsdocTags: [
{
name: 'see',
comment:
'[Host view using `ViewContainerRef.createComponent`](guide/components/programmatic-rendering#host-view-using-viewcontainerrefcreatecomponent)',
},
{
name: 'see',
comment:
'[Popup attached to `document.body` with `createComponent` + `hostElement`](guide/components/programmatic-rendering#popup-attached-to-documentbody-with-createcomponent--hostelement)',
},
{
name: 'see',
comment: '[Method with `backticks` in title](https://example.com "Title with `code`")',
},
],
moduleName: 'test',
});
setSymbols(
Object.fromEntries([
['Route', 'test'],
['Router', 'angular/router'],
['Router.someMethod', 'test'],
['Router.someMethodWithParenthesis', 'test'],
['FormGroup', 'test'],
['FormGroup.someMethod', 'test'],
]),
);
expect(entry.additionalLinks[0]).toEqual({
label: 'Host view using <code>ViewContainerRef.createComponent</code>',
url: 'guide/components/programmatic-rendering#host-view-using-viewcontainerrefcreatecomponent',
title: undefined,
});
const entry = addHtmlDescription({
description: `
expect(entry.additionalLinks[1]).toEqual({
label:
'Popup attached to <code>document.body</code> with <code>createComponent</code> + <code>hostElement</code>',
url: 'guide/components/programmatic-rendering#popup-attached-to-documentbody-with-createcomponent--hostelement',
title: undefined,
});
expect(entry.additionalLinks[2]).toEqual({
label: 'Method with <code>backticks</code> in title',
url: 'https://example.com',
title: 'Title with `code`',
});
});
it('should throw on invalid relatie @link', () => {
const entryFn = () =>
addHtmlAdditionalLinks({
jsdocTags: [
{
name: 'see',
comment: '{@link cli/build ng build}',
},
],
moduleName: 'test',
});
expect(entryFn).toThrowError(/Forbidden relative link: cli\/build ng build/);
});
it('should parse markdown in descriptions', async () => {
setHighlighterInstance(await initHighlighter());
setSymbols(
Object.fromEntries([
['Route', 'test'],
['Router', 'angular/router'],
['Router.someMethod', 'test'],
['Router.someMethodWithParenthesis', 'test'],
['FormGroup', 'test'],
['FormGroup.someMethod', 'test'],
]),
);
const entry = addHtmlDescription({
description: `
\`\`\`angular-ts
import { Router } from '@angular/router';
@@ -226,36 +223,16 @@ function setupRouter() {
}
\`\`\`
`,
moduleName: 'test',
});
// Should have some shiki variables (meaning the description was highlighted).
expect(entry.htmlDescription).toContain('--shiki');
// Having docs-code means that the description was parsed and formatted correctly (by the shared marked renderer)
expect(entry.htmlDescription).toContain('class="docs-code"');
expect(entry.htmlDescription).toContain('/api/angular/router/Router');
moduleName: 'test',
});
it('should transform entry with different description & description tag', () => {
const entry = addHtmlDescription({
'description':
"Enables interop with the browser's `Navigation` API for router navigations.",
'jsdocTags': [
{
'name': 'description',
'comment': 'This feature is _highly_ experimental ...',
},
],
'moduleName': 'platform-browser',
});
// Should have some shiki variables (meaning the description was highlighted).
expect(entry.htmlDescription).toContain('--shiki');
expect(entry.htmlDescription).toBe(`<p>This feature is <em>highly</em> experimental ...</p>`);
expect(entry.shortHtmlDescription).toBe(
`<p>Enables interop with the browser's <code>Navigation</code> API for router navigations.</p>`,
);
});
// Having docs-code means that the description was parsed and formatted correctly (by the shared marked renderer)
expect(entry.htmlDescription).toContain('class="docs-code"');
expect(entry.htmlDescription).toContain('/api/angular/router/Router');
});
it('should only mark as deprecated if all overloads are deprecated', () => {
@@ -27,7 +27,7 @@ export function getCliRenderable(command: CliCommand): CliCommandRenderable {
}),
cards: getCliCardsRenderable(command),
argumentsLabel: getArgumentsLabel(command),
optionsLabel: getOptionsLabel(command),
hasOptions: getOptions(command).length > 0,
};
}
@@ -66,12 +66,7 @@ function getArgumentsLabel(command: CliCommand): string {
if (args.length === 0) {
return '';
}
const label = command.command.replace(/^ng\s+/, '').replace(`${command.name} `, '');
return ` ${label}`;
}
function getOptionsLabel(command: CliCommand): string {
return getOptions(command).length > 0 ? ' [options]' : '';
return command.command.replace(`${command.name} `, '');
}
function getArgs(command: CliCommand): CliOption[] {
@@ -39,7 +39,7 @@ import {
insertParenthesesForDecoratorInShikiHtml,
replaceKeywordFromShikiHtml,
} from '../shiki/shiki.mjs';
import {getSymbolsAsApiEntries, getSymbolUrl} from '../symbol-context.mjs';
import {getSymbolUrl} from '../symbol-context.mjs';
import {codeToHtml} from '../../../shared/shiki.mjs';
import {formatJs} from './format-code.mjs';
@@ -92,10 +92,7 @@ export async function addRenderableCodeToc<T extends DocEntry & HasModuleName>(
let codeWithSyntaxHighlighting = codeToHtml(
getHighlighterInstance(),
formattedCode ?? metadata?.contents,
{
language: 'typescript',
apiEntries: getSymbolsAsApiEntries(),
},
{language: 'typescript'},
);
if (isDecoratorEntry(entry)) {
@@ -114,6 +111,11 @@ export async function addRenderableCodeToc<T extends DocEntry & HasModuleName>(
// }
}
// Note: Don't expect enum value in signatures to be linked correctly
// as shiki already splits them into separate span blocks.
// Only the enum itself will recieve a link
codeWithSyntaxHighlighting = addApiLinksToHtml(codeWithSyntaxHighlighting);
// shiki returns the lines wrapped by 2 node : 1 pre node, 1 code node.
// As leveraging jsdom isn't trivial here, we rely on a regex to extract the line nodes
const pattern = /(.*?)<code.*?>(.*?)<\/code>(.*)/s;
@@ -22,7 +22,6 @@ import {
HasModuleName,
HasRenderableJsDocTags,
HasStableFlag,
MaybeJsDocTags,
} from '../entities/traits.mjs';
import {parseMarkdown} from '../../../shared/marked/parse.mjs';
@@ -44,7 +43,7 @@ const jsDoclinkRegex = /\{\s*@link\s+([^}]+)\s*\}/;
const jsDoclinkRegexGlobal = new RegExp(jsDoclinkRegex.source, 'g');
/** Given an entity with a description, gets the entity augmented with an `htmlDescription`. */
export function addHtmlDescription<T extends HasDescription & HasModuleName & MaybeJsDocTags>(
export function addHtmlDescription<T extends HasDescription & HasModuleName>(
entry: T,
): T & HasHtmlDescription {
const firstParagraphRule = /(.*?)(?:\n\n|$)/s;
@@ -57,17 +56,10 @@ export function addHtmlDescription<T extends HasDescription & HasModuleName & Ma
?.comment ?? '';
}
let description = entry.description || jsDocDescription;
let shortDescription = description.match(firstParagraphRule)?.[0] ?? '';
// For the cases where the @description tag is after a short description
if (jsDocDescription && description !== jsDocDescription) {
shortDescription = entry.description;
description = jsDocDescription;
}
const description = !!entry.description ? entry.description : jsDocDescription;
const shortTextMatch = description.match(firstParagraphRule);
const htmlDescription = getHtmlForJsDocText(description).trim();
const shortHtmlDescription = getHtmlForJsDocText(shortDescription).trim();
const shortHtmlDescription = getHtmlForJsDocText(shortTextMatch ? shortTextMatch[0] : '').trim();
return {...entry, htmlDescription, shortHtmlDescription};
}
@@ -145,9 +137,6 @@ function getHtmlAdditionalLinks<T extends HasJsDocTags>(entry: T): LinkEntryRend
.filter((tag) => tag.name === JS_DOC_SEE_TAG)
.map((tag) => tag.comment)
.map((comment) => {
// TODO: Throw when the comment is an absolute link.
// With TS 5.9 this is not possible as the ts api that extracts comments from tags strips the "http" part of links.
const markdownLinkMatch = comment.match(markdownLinkRule);
if (markdownLinkMatch) {
@@ -213,6 +202,10 @@ function convertLinks(text: string) {
}
function parseAtLink(link: string): {label: string; url: string} | undefined {
// Because of microsoft/TypeScript/issues/59679
// getTextOfJSDocComment introduces an extra space between the symbol and a trailing ()
link = link.replace(/ \(\)$/, '');
let [rawSymbol, description] = link.split(/\s(.+)/);
if (rawSymbol.startsWith('#')) {
rawSymbol = rawSymbol.substring(1);
@@ -14,7 +14,6 @@ esbuild(
external = [
"jsdom",
"playwright-core",
"typescript",
],
format = "esm",
output = "guides.mjs",
@@ -39,7 +38,6 @@ esbuild(
entry_point = ":index.mts",
external = [
"jsdom",
"typescript",
],
output = "guides-no-mermaid.mjs",
platform = "node",
@@ -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"],
@@ -26,9 +17,7 @@ ts_project(
"//adev/shared-docs/pipeline:__subpackages__",
],
deps = [
":linking",
"//adev:node_modules/shiki",
"//adev:node_modules/typescript",
],
)
@@ -45,17 +34,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
}
@@ -23,15 +23,6 @@ const LINK_EXEMPT = new Set([
'animation',
'transition',
'trigger',
'group()',
'keyframes',
'@keyframes',
'input',
'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="([^"]*)"/;
@@ -6,9 +6,9 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {RendererThis, Token, TokenizerThis, Tokens} from 'marked';
import {anchorTarget, loadWorkspaceRelativeFile} from '../../helpers.mjs';
import {AdevDocsRenderer} from '../../renderer.mjs';
import {Tokens, Token, RendererThis, TokenizerThis} from 'marked';
import {loadWorkspaceRelativeFile, anchorTarget} from '../../helpers.mjs';
import {setInsideLink} from '../../transformations/link.mjs';
interface DocsCardToken extends Tokens.Generic {
type: 'docs-card';
@@ -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="([^"]*)"/;
@@ -67,13 +67,11 @@ export const docsCardExtension = {
return undefined;
},
renderer(this: RendererThis, token: DocsCardToken) {
return token.imgSrc
? getCardWithSvgIllustration(this, token)
: getStandardCard(this.parser.renderer as AdevDocsRenderer, token);
return token.imgSrc ? getCardWithSvgIllustration(this, token) : getStandardCard(this, token);
},
};
function getStandardCard(renderer: AdevDocsRenderer, token: DocsCardToken) {
function getStandardCard(renderer: RendererThis, token: DocsCardToken) {
if (token.iconImgSrc && token.href) {
// We can assume that all icons are svg files since they are custom.
// We need to read svg content, instead of renering svg with `img`,
@@ -112,11 +110,13 @@ function getStandardCard(renderer: AdevDocsRenderer, token: DocsCardToken) {
`;
}
function parseWithoutCreatingLinks(renderer: AdevDocsRenderer, token: DocsCardToken) {
renderer.context.disableAutoLinking = true;
const parsed = renderer.parser.parse(token.tokens);
renderer.context.disableAutoLinking = false;
return parsed;
function parseWithoutCreatingLinks(renderer: RendererThis, token: DocsCardToken) {
setInsideLink(true);
try {
return renderer.parser.parse(token.tokens);
} finally {
setInsideLink(false);
}
}
function getCardWithSvgIllustration(renderer: RendererThis, token: DocsCardToken) {
@@ -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(' '),
};
@@ -11,18 +11,13 @@ import {expandRangeStringValues} from './range.mjs';
import {JSDOM} from 'jsdom';
import {HighlighterGeneric} from 'shiki';
import {codeToHtml} from '../../../../shiki.mjs';
import {RendererContext} from '../../../renderer.mjs';
const lineNumberClassName: string = 'shiki-ln-number';
/**
* Updates the provided token's code value to include syntax highlighting.
*/
export function highlightCode(
highlighter: HighlighterGeneric<any, any>,
token: CodeToken,
context: RendererContext,
) {
export function highlightCode(highlighter: HighlighterGeneric<any, any>, token: CodeToken) {
// TODO(josephperrott): Handle mermaid usages i.e. language == mermaidClassName
if (token.language !== 'none' && token.language !== 'file') {
// Decode the code content to replace HTML entities to characters
@@ -32,11 +27,7 @@ export function highlightCode(
? new Set(expandRangeStringValues(token.highlight))
: undefined;
token.code = codeToHtml(highlighter, token.code, {
language,
highlight,
apiEntries: context.apiEntries,
});
token.code = codeToHtml(highlighter, token.code, {language, highlight});
}
if (token.linenums) {
@@ -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[];
@@ -52,7 +50,7 @@ export function formatCode(token: CodeToken, context: RendererContext): string {
}
extractRegions(token);
highlightCode(context.highlighter, token, context);
highlightCode(context.highlighter, token);
const containerEl = JSDOM.fragment(`
<div class="docs-code${token.style ? ' docs-code-' + token.style : ''}">
@@ -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>
`;
},
@@ -31,8 +31,6 @@ export interface RendererContext {
apiEntries?: Record<string, {moduleName: string; aliases?: string[]}>;
highlighter: HighlighterGeneric<any, any>;
headerIds: Map<string, number>;
/** In the case we want to disable auto-linking because anchor blocks might be incompatible where some code blocks are being rendered */
disableAutoLinking: boolean;
}
/**
@@ -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', () => {
@@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {parseMarkdown} from '../../parse.mjs';
import {resolve} from 'node:path';
import {readFile} from 'fs/promises';
import {JSDOM} from 'jsdom';
import {resolve} from 'node:path';
import {parseMarkdown} from '../../parse.mjs';
import {rendererContext} from '../renderer-context.mjs';
describe('markdown to html', () => {
@@ -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', () => {
@@ -122,17 +118,4 @@ describe('markdown to html', () => {
expect(h2.firstElementChild!.innerHTML).toBe('Query for the <code>&lt;h1&gt;</code>');
});
it('shoud now link symbols in headings', () => {
const markdownDocument = JSDOM.fragment(
parseMarkdown('## Hello **NEW** `Router` ', rendererContext),
);
const h2 = markdownDocument.querySelector('h2')!;
// The anchor element should be to only child, no nested anchor
expect(h2.children.length).toBe(1);
// We ensure that we still style the heading content
expect(markdownDocument.querySelector('strong')).toBeDefined();
});
});
@@ -25,7 +25,6 @@ export const rendererContext: RendererContext = {
Router: {moduleName: 'angular/router'},
},
headerIds: new Map<string, number>(),
disableAutoLinking: false,
};
export async function setHighlighter() {
@@ -8,15 +8,12 @@
import {Tokens} from 'marked';
import {AdevDocsRenderer} from '../renderer.mjs';
import {getIdFromHeading} from '../../heading.mjs';
export function headingRender(
this: AdevDocsRenderer,
{depth, tokens, text: headingText, raw}: Tokens.Heading,
): string {
this.context.disableAutoLinking = true;
const parsedText = this?.parser.parseInline(tokens, this);
this.context.disableAutoLinking = false;
const parsedText = this?.parser.parseInline(tokens);
if (depth === 1) {
return `
<header class="docs-header">
@@ -26,11 +23,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 +63,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
}
@@ -6,10 +6,20 @@
* found in the LICENSE file at https://angular.dev/license
*/
import {Tokens} from 'marked';
import {anchorTarget} from '../helpers.mjs';
import {Renderer, Tokens} from 'marked';
import {AdevDocsRenderer} from '../renderer.mjs';
/**
* Tracks whether the current renderer is inside a link.
*
* This is necessary because nested links are invalid HTML and can cause rendering issues.
*/
let insideLink = false;
export function setInsideLink(value: boolean) {
insideLink = value;
}
export function linkRender(this: AdevDocsRenderer, {href, title, tokens}: Tokens.Link) {
// We have render-time check that we don't create absolute links (which are rendered as external links)
// in our guides
@@ -30,7 +40,7 @@ export function linkRender(this: AdevDocsRenderer, {href, title, tokens}: Tokens
);
}
if (this.context.disableAutoLinking) {
if (insideLink) {
return this.parser.parseInline(tokens);
}
+3 -45
View File
@@ -6,11 +6,8 @@
* found in the LICENSE file at https://angular.dev/license
*/
import ts from 'typescript';
import {HighlighterGeneric, ShikiTransformer} from 'shiki';
import {ApiEntries, getSymbolUrl} from './linking.mjs';
const scanner = ts.createScanner(ts.ScriptTarget.Latest, true);
const LIGHT_THEME = 'github-light';
const DARK_THEME = 'github-dark';
@@ -39,7 +36,6 @@ export function codeToHtml(
highlighter: HighlighterGeneric<any, any>,
code: string,
config: {
apiEntries?: ApiEntries;
language?: string;
highlight?: Set<number>;
},
@@ -52,12 +48,9 @@ export function codeToHtml(
},
cssVariablePrefix: '--shiki-',
defaultColor: false,
transformers: [
removeWhitespaceTransformer(),
highlightTransformer(config.highlight),
linkApiEntriesTransformer(config.apiEntries),
],
transformers: [removeWhitespaceTransformer(), highlightTransformer(config.highlight)],
});
return html;
}
@@ -73,7 +66,7 @@ function highlightTransformer(highlight?: Set<number>): ShikiTransformer {
}
/** A custom transformer which removes all of the whitespace between lines of code in the generated output. */
function removeWhitespaceTransformer(): ShikiTransformer {
function removeWhitespaceTransformer(highlight?: Set<number>): ShikiTransformer {
return {
code(code) {
code.children = code.children.filter(
@@ -82,38 +75,3 @@ function removeWhitespaceTransformer(): ShikiTransformer {
},
};
}
/** A custom transformer which adds a link to local API entries whenever a matching identifier is discovered in the code block. */
function linkApiEntriesTransformer(apiEntries?: ApiEntries): ShikiTransformer {
if (apiEntries === undefined) {
return {};
}
return {
preprocess(code, options) {
options.decorations ??= [];
scanner.setText(code);
let token = scanner.scan();
while (token !== ts.SyntaxKind.EndOfFileToken) {
if (token === ts.SyntaxKind.Identifier) {
const symbolUrl = getSymbolUrl(scanner.getTokenText(), apiEntries);
if (symbolUrl !== undefined) {
options.decorations.push({
transform: (el) => {
el.tagName = 'a';
el.properties['href'] = symbolUrl;
return el;
},
start: scanner.getTokenStart(),
end: scanner.getTokenEnd(),
});
}
}
token = scanner.scan();
}
return code;
},
};
}
@@ -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');
});
});
@@ -47,7 +47,7 @@
"polyfills": ["zone.js"],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": ["src/favicon.ico", "src/assets", "src/public"],
"assets": ["src/favicon.ico", "src/assets"],
"styles": ["src/styles.css"],
"scripts": []
},
+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 {
+2 -2
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;
@@ -260,7 +260,7 @@
background-color: var(--septenary-contrast);
}
&:focus-visible {
&:focus {
background-color: var(--senary-contrast);
span {
+1 -1
View File
@@ -350,7 +350,7 @@
.ͼ1 .cm-button {
background-image: linear-gradient(var(--octonary-contrast), var(--page-background));
&:focus-visible {
&:focus {
background-image: linear-gradient(var(--senary-contrast), var(--page-background));
}
}

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