diff --git a/.bazelversion b/.bazelversion
index f9c71a52e2fd..acd405b1d62e 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-8.5.1
+8.6.0
diff --git a/.gemini/config.yaml b/.gemini/config.yaml
new file mode 100644
index 000000000000..9f4eb5f02da3
--- /dev/null
+++ b/.gemini/config.yaml
@@ -0,0 +1,11 @@
+have_fun: false
+code_review:
+ disable: false
+ comment_severity_threshold: MEDIUM
+ max_review_comments: -1
+ pull_request_opened:
+ help: false
+ summary: false
+ code_review: true
+ include_drafts: false
+ignore_patterns: []
diff --git a/.github/SAVED_REPLIES.md b/.github/SAVED_REPLIES.md
index 1237bc279e11..dfce61802c0e 100644
--- a/.github/SAVED_REPLIES.md
+++ b/.github/SAVED_REPLIES.md
@@ -99,3 +99,13 @@ I'd like to remind everyone that **you only have reproducible installs if you us
**It is your responsibility as a library consumer to use lockfiles**. No one wants to do a release with bugs but it sometimes happens, and the best we can do is to fix it as fast as possible with a new release. When you have a couple of thousand total dependencies it is only a matter of time until one of them has a bad release.
```
+
+## Angular CLI: Spam (v1)
+
+```
+Woah, looks like you've opened a lot of issues/PRs recently. While we appreciate contributions from the community, triaging and reviewing a large influx of content in a short time period takes time away from other ongoing projects. As a result, we're closing these issues/PRs to maintain the team's focus.
+
+Note that this is not necessarily a rejection of the goals or direction of any of these contributions in particular, so much as a reflection of the team's current capacity and priorities.
+
+You are welcome to open a smaller subset of issues/PRs in accordance with [our policy](https://github.com/angular/angular/blob/main/contributing-docs/spam.md) focused on the most important and impactful contributions and we will do our best to prioritize a response as soon as possible.
+```
diff --git a/.github/_CODEOWNERS.tmp b/.github/_CODEOWNERS.tmp
deleted file mode 100644
index 3e79d395914f..000000000000
--- a/.github/_CODEOWNERS.tmp
+++ /dev/null
@@ -1,17 +0,0 @@
-/packages/_/ @hansl @clydin
-/packages/angular/pwa @hansl @Brocco
-/packages/angular_devkit/architect/ @filipesilva @hansl
-/packages/angular_devkit/architect_cli/ @filipesilva @hansl
-/packages/angular_devkit/build_angular/ @filipesilva @clydin
-/packages/angular_devkit/build_ng_packagr/ @filipesilva @clydin
-/packages/angular_devkit/build_optimizer/ @filipesilva @clydin
-/packages/angular_devkit/core/ @hansl @clydin
-/packages/angular_devkit/schematics/ @hansl @Brocco
-/packages/angular_devkit/schematics_cli/ @hansl @Brocco
-/packages/ngtools/webpack/ @hansl @filipesilva @clydin
-/packages/schematics/angular/ @hansl @Brocco
-/packages/schematics/package_update/ @hansl @Brocco
-/packages/schematics/schematics/ @hansl @Brocco
-/packages/schematics/update/ @hansl @Brocco
-/rules/ @hansl @clydin
-/scripts/ @hansl @clydin
diff --git a/.github/workflows/assistant-to-the-branch-manager.yml b/.github/workflows/assistant-to-the-branch-manager.yml
index 6e0a4cb8c7d7..87519e50b728 100644
--- a/.github/workflows/assistant-to-the-branch-manager.yml
+++ b/.github/workflows/assistant-to-the-branch-manager.yml
@@ -17,6 +17,6 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- - uses: angular/dev-infra/github-actions/branch-manager@469708f109a90884ca403d150d33079a3a5a8769
+ - uses: angular/dev-infra/github-actions/branch-manager@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5f62362d4410..1617dbb0d061 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -21,9 +21,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
- uses: angular/dev-infra/github-actions/bazel/setup@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Generate JSON schema types
@@ -44,11 +44,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
- uses: angular/dev-infra/github-actions/bazel/setup@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
- uses: angular/dev-infra/github-actions/bazel/configure-remote@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -61,11 +61,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
- uses: angular/dev-infra/github-actions/bazel/setup@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
- uses: angular/dev-infra/github-actions/bazel/configure-remote@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -78,19 +78,19 @@ jobs:
strategy:
fail-fast: false
matrix:
- node: [20, 22, 24]
+ node: [22, 24]
subset: [esbuild, webpack]
shard: [0, 1, 2, 3, 4, 5]
runs-on: ubuntu-latest
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Setup Bazel
- uses: angular/dev-infra/github-actions/bazel/setup@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
- uses: angular/dev-infra/github-actions/bazel/configure-remote@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Run CLI E2E tests
@@ -100,11 +100,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
- uses: angular/dev-infra/github-actions/bazel/setup@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
- uses: angular/dev-infra/github-actions/bazel/configure-remote@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Install node modules
@@ -117,7 +117,7 @@ jobs:
//tests:e2e.esbuild_node24 \
--platforms=tools:windows_x64
- name: Store built Windows E2E tests
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: win-e2e-build-artifacts
path: |
@@ -137,11 +137,11 @@ jobs:
runs-on: windows-2025
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Download built Windows E2E tests
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: win-e2e-build-artifacts
path: dist/bin/tests/
@@ -164,13 +164,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Setup Bazel
- uses: angular/dev-infra/github-actions/bazel/setup@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
- uses: angular/dev-infra/github-actions/bazel/configure-remote@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Run CLI E2E tests
@@ -188,13 +188,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Setup Bazel
- uses: angular/dev-infra/github-actions/bazel/setup@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
- uses: angular/dev-infra/github-actions/bazel/configure-remote@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Run CLI E2E tests
@@ -208,13 +208,13 @@ jobs:
SAUCE_TUNNEL_IDENTIFIER: angular-cli-${{ github.workflow }}-${{ github.run_number }}
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Setup Bazel
- uses: angular/dev-infra/github-actions/bazel/setup@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
- uses: angular/dev-infra/github-actions/bazel/configure-remote@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
- name: Run E2E Browser tests
@@ -231,7 +231,7 @@ jobs:
./scripts/saucelabs/wait-for-tunnel.sh
pnpm bazel test --config=saucelabs //tests:e2e.saucelabs
./scripts/saucelabs/stop-tunnel.sh
- - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
+ - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
if: ${{ failure() }}
with:
name: sauce-connect-log
@@ -244,11 +244,11 @@ jobs:
CIRCLE_BRANCH: ${{ github.ref_name }}
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Setup Bazel
- uses: angular/dev-infra/github-actions/bazel/setup@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- run: pnpm admin snapshots --verbose
env:
SNAPSHOT_BUILDS_GITHUB_TOKEN: ${{ secrets.SNAPSHOT_BUILDS_GITHUB_TOKEN }}
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 0b070c5aae16..2f8871e567c0 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -23,12 +23,12 @@ jobs:
with:
persist-credentials: false
- name: Initialize CodeQL
- uses: github/codeql-action/init@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2
+ uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
with:
languages: javascript-typescript
build-mode: none
config-file: .github/codeql/config.yml
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2
+ uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
with:
category: '/language:javascript-typescript'
diff --git a/.github/workflows/dev-infra.yml b/.github/workflows/dev-infra.yml
index d8d2f0b7bb6c..8336a6ba0f4d 100644
--- a/.github/workflows/dev-infra.yml
+++ b/.github/workflows/dev-infra.yml
@@ -3,6 +3,8 @@ name: DevInfra
on:
pull_request_target:
types: [opened, synchronize, reopened]
+ issues:
+ types: [opened, reopened]
# Declare default permissions as read only.
permissions:
@@ -10,16 +12,24 @@ permissions:
jobs:
labels:
+ 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/pull-request-labeling@469708f109a90884ca403d150d33079a3a5a8769
+ - uses: angular/dev-infra/github-actions/labeling/pull-request@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
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@469708f109a90884ca403d150d33079a3a5a8769
+ - uses: angular/dev-infra/github-actions/post-approval-changes@ba726e7bca0b08b125ccc6f93c233749e1213c17
with:
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+ issue_labels:
+ if: github.event_name == 'issues'
+ runs-on: ubuntu-latest
+ steps:
+ - uses: angular/dev-infra/github-actions/labeling/issue@ba726e7bca0b08b125ccc6f93c233749e1213c17
+ with:
+ angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
+ google-generative-ai-key: ${{ secrets.GOOGLE_GENERATIVE_AI_KEY }}
diff --git a/.github/workflows/feature-requests.yml b/.github/workflows/feature-requests.yml
deleted file mode 100644
index 4e22f1a5eda8..000000000000
--- a/.github/workflows/feature-requests.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-name: Feature request triage bot
-
-# Declare default permissions as read only.
-permissions:
- contents: read
-
-on:
- schedule:
- # Run at 13:00 every day
- - cron: '0 13 * * *'
-
-jobs:
- feature_triage:
- # To prevent this action from running in forks, we only run it if the repository is exactly the
- # angular/angular-cli repository.
- if: github.repository == 'angular/angular-cli'
- runs-on: ubuntu-latest
- steps:
- - uses: angular/dev-infra/github-actions/feature-request@469708f109a90884ca403d150d33079a3a5a8769
- with:
- angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
diff --git a/.github/workflows/perf.yml b/.github/workflows/perf.yml
index 9dbf67608b8c..dac67ebcbe7f 100644
--- a/.github/workflows/perf.yml
+++ b/.github/workflows/perf.yml
@@ -23,7 +23,7 @@ jobs:
workflows: ${{ steps.workflows.outputs.workflows }}
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- id: workflows
@@ -38,9 +38,9 @@ jobs:
workflow: ${{ fromJSON(needs.list.outputs.workflows) }}
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
- uses: angular/dev-infra/github-actions/bazel/setup@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
# We utilize the google-github-actions/auth action to allow us to get an active credential using workflow
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
index 12195b663fda..1015afa28ef7 100644
--- a/.github/workflows/pr.yml
+++ b/.github/workflows/pr.yml
@@ -23,7 +23,7 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
+ - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: filter
with:
filters: |
@@ -34,11 +34,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
- uses: angular/dev-infra/github-actions/bazel/setup@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup ESLint Caching
- uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
+ uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: .eslintcache
key: ${{ runner.os }}-${{ hashFiles('.eslintrc.json') }}
@@ -66,23 +66,23 @@ jobs:
# it has been merged.
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@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/linting/licenses@ba726e7bca0b08b125ccc6f93c233749e1213c17
build:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
- uses: angular/dev-infra/github-actions/bazel/setup@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
- uses: angular/dev-infra/github-actions/bazel/configure-remote@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Build release targets
run: pnpm ng-dev release build
- name: Store PR release packages
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: packages
path: dist/releases/*.tgz
@@ -93,11 +93,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
- uses: angular/dev-infra/github-actions/bazel/setup@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
- uses: angular/dev-infra/github-actions/bazel/configure-remote@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Run module and package tests
@@ -114,13 +114,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Setup Bazel
- uses: angular/dev-infra/github-actions/bazel/setup@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
- uses: angular/dev-infra/github-actions/bazel/configure-remote@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Run CLI E2E tests
run: pnpm bazel test --test_env=E2E_SHARD_TOTAL=6 --test_env=E2E_SHARD_INDEX=${{ matrix.shard }} --config=e2e //tests:e2e.${{ matrix.subset }}_node${{ matrix.node }}
@@ -128,11 +128,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel
- uses: angular/dev-infra/github-actions/bazel/setup@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
- uses: angular/dev-infra/github-actions/bazel/configure-remote@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Build E2E tests for Windows on Linux
@@ -142,7 +142,7 @@ jobs:
//tests:e2e.esbuild_node24 \
--platforms=tools:windows_x64
- name: Store built Windows E2E tests
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: win-e2e-build-artifacts
path: |
@@ -156,11 +156,11 @@ jobs:
runs-on: windows-2025
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Download built Windows E2E tests
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: win-e2e-build-artifacts
path: dist/bin/tests/
@@ -183,13 +183,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Setup Bazel
- uses: angular/dev-infra/github-actions/bazel/setup@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
- uses: angular/dev-infra/github-actions/bazel/configure-remote@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Run CLI E2E tests
run: pnpm bazel test --test_env=E2E_SHARD_TOTAL=3 --test_env=E2E_SHARD_INDEX=${{ matrix.shard }} --config=e2e //tests:e2e.${{ matrix.subset }}_node${{ matrix.node }}
@@ -205,12 +205,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Initialize environment
- uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Install node modules
run: pnpm install --frozen-lockfile
- name: Setup Bazel
- uses: angular/dev-infra/github-actions/bazel/setup@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/setup@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Setup Bazel RBE
- uses: angular/dev-infra/github-actions/bazel/configure-remote@469708f109a90884ca403d150d33079a3a5a8769
+ uses: angular/dev-infra/github-actions/bazel/configure-remote@ba726e7bca0b08b125ccc6f93c233749e1213c17
- name: Run CLI E2E tests
run: pnpm bazel test --test_env=E2E_SHARD_TOTAL=6 --test_env=E2E_SHARD_INDEX=${{ matrix.shard }} --config=e2e //tests:e2e.snapshots.${{ matrix.subset }}_node${{ matrix.node }}
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index 4c9465d0cf9b..10f1a668575e 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -38,7 +38,7 @@ jobs:
# Upload the results as artifacts.
- name: 'Upload artifact'
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
+ uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: SARIF file
path: results.sarif
@@ -46,6 +46,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: 'Upload to code-scanning'
- uses: github/codeql-action/upload-sarif@45cbd0c69e560cd9e7cd7f8c32362050c9b7ded2 # v4.32.2
+ uses: github/codeql-action/upload-sarif@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
with:
sarif_file: results.sarif
diff --git a/.monorepo.json b/.monorepo.json
index 4d5face644df..ad86e09acdeb 100644
--- a/.monorepo.json
+++ b/.monorepo.json
@@ -59,11 +59,6 @@
],
"snapshotRepo": "angular/angular-devkit-architect-builds"
},
- "@angular-devkit/architect-cli": {
- "name": "Architect CLI",
- "section": "Tooling",
- "snapshotRepo": "angular/angular-devkit-architect-cli-builds"
- },
"@angular-devkit/build-angular": {
"name": "Build Angular",
"links": [
diff --git a/.ng-dev/release.mjs b/.ng-dev/release.mjs
index 2aadf9db122c..4d4a4fc4b927 100644
--- a/.ng-dev/release.mjs
+++ b/.ng-dev/release.mjs
@@ -23,12 +23,7 @@ export const release = {
await assertValidDependencyRanges(newVersion, releasePackages);
},
releaseNotes: {
- groupOrder: [
- '@angular/cli',
- '@schematics/angular',
- '@angular-devkit/architect-cli',
- '@angular-devkit/schematics-cli',
- ],
+ groupOrder: ['@angular/cli', '@schematics/angular', '@angular-devkit/schematics-cli'],
},
publishRegistry: 'https://wombat-dressing-room.appspot.com',
releasePrLabels: ['action: merge'],
diff --git a/.nvmrc b/.nvmrc
index 85e502778f62..db49bb14d78e 100644
--- a/.nvmrc
+++ b/.nvmrc
@@ -1 +1 @@
-22.22.0
+22.22.2
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b11c764b3618..f3779f5da66c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,112 +1,571 @@
-
+
-# 19.2.20 (2026-02-13)
+# 22.0.0-next.5 (2026-04-08)
+
+## Breaking Changes
+
+### @angular/build
+
+- `istanbul-lib-instrument` is now an optional peer dependency.
+ Projects using karma with code coverage enabled will need to ensure that istanbul-lib-instrument is installed. Note: `ng update` will automatically add this dependency during the update process.
+
+### @schematics/angular
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------- |
+| [be60a63b7](https://github.com/angular/angular-cli/commit/be60a63b7b8fdea26bfd3329ce54d321334db2d2) | feat | add migrate-karma-to-vitest update migration |
+| [43505066e](https://github.com/angular/angular-cli/commit/43505066e2350ca875051bb0f9991da8003c9247) | feat | add migration to add istanbul-lib-instrument |
+| [470e1f937](https://github.com/angular/angular-cli/commit/470e1f937492e73971dea9c39af83368caf74e42) | fix | add istanbul-lib-instrument to application/library generator dependencies |
+
+### @angular/cli
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------------------- |
+| [422c8183e](https://github.com/angular/angular-cli/commit/422c8183ea3596660475491b9630df34276c468e) | fix | handle missing package manager during analytics initialization |
+
+### @angular/build
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------------- |
+| [3007f46b7](https://github.com/angular/angular-cli/commit/3007f46b7e077227b17d8bb1090edd2f8ff19ae3) | feat | move istanbul-lib-instrument to optional peer dependency |
+| [829bdc61d](https://github.com/angular/angular-cli/commit/829bdc61dd77231bb13f01efd052811661fe4f48) | fix | preserve coverage ignore comments in development |
+| [e2f95fc19](https://github.com/angular/angular-cli/commit/e2f95fc19a648f3da84b58ace91283d0fa422cc1) | fix | show clear error when styleUrl points to a TypeScript file |
+
+
+
+
+
+# 21.2.7 (2026-04-08)
+
+### @angular/cli
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------------------- |
+| [971041aa1](https://github.com/angular/angular-cli/commit/971041aa11e4546772e4677b77ceec2ad1374496) | fix | handle missing package manager during analytics initialization |
+
+### @angular/build
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------------- |
+| [365cce81d](https://github.com/angular/angular-cli/commit/365cce81dde91463d4f0049a69feaed018902585) | fix | preserve coverage ignore comments in development |
+| [9f74660c3](https://github.com/angular/angular-cli/commit/9f74660c3688bb1f0d6a7608a136a30dbfe4d7e9) | fix | show clear error when styleUrl points to a TypeScript file |
+
+
+
+
+
+# 20.3.23 (2026-04-08)
+
+### @angular/build
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ---------------------- |
+| [ccab02ba0](https://github.com/angular/angular-cli/commit/ccab02ba0413f25464a6e4cb5871716b221013b7) | fix | update vite to `7.3.2` |
+
+
+
+
+
+# 19.2.24 (2026-04-08)
+
+### @angular/build
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ---------------------- |
+| [f4595d599](https://github.com/angular/angular-cli/commit/f4595d599001713ae978865d7f84e33e5a9a077a) | fix | update vite to `6.4.2` |
+
+
+
+
+
+# 22.0.0-next.4 (2026-04-01)
+
+### @angular/cli
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------- |
+| [87d99e98b](https://github.com/angular/angular-cli/commit/87d99e98b178c8f7d5e944a346faf70c51bdfcef) | feat | support custom port in MCP devserver start tool |
+| [4815a5417](https://github.com/angular/angular-cli/commit/4815a5417c7a0135fb66149c2e4c1008e21e3a26) | fix | fix sourceRoot resolution for MCP projects tool |
+
+### @angular/build
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------- |
+| [21d8aa474](https://github.com/angular/angular-cli/commit/21d8aa4747573132476c3a0a4b7ea1f6405a71ef) | fix | ensure transitive SCSS partial errors are tracked in watch mode |
+| [e558117b7](https://github.com/angular/angular-cli/commit/e558117b748ee5837324d49466108d21db596b2e) | fix | ensure Vitest mock patching is executed only once |
+| [81e4faae7](https://github.com/angular/angular-cli/commit/81e4faae7699e2ed1eb8f4656dc115ca9c20f416) | fix | preserve error stack traces during prerendering |
+| [8dd341e21](https://github.com/angular/angular-cli/commit/8dd341e21b8f44e8e2bf3f322cced8ff6e861098) | fix | scope CHROME_BIN executable path to individual playwright instances |
+
+### @angular/ssr
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------- |
+| [e7e434ca3](https://github.com/angular/angular-cli/commit/e7e434ca3d78a8369b592bf870c9466962f77c94) | fix | allow underscores in host validation |
+| [bcd99f944](https://github.com/angular/angular-cli/commit/bcd99f944ecb90f896040030b98f1d03692b5b6f) | fix | patch Headers.forEach in cloneRequestAndPatchHeaders |
+
+
+
+
+
+# 21.2.6 (2026-04-01)
+
+### @angular/cli
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------- |
+| [ea14f28cc](https://github.com/angular/angular-cli/commit/ea14f28ccfc6e5534eaef516bf1bfbe21582da04) | fix | fix sourceRoot resolution for MCP projects tool |
+
+### @angular/build
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------- |
+| [9136eb376](https://github.com/angular/angular-cli/commit/9136eb37630d6315891b3c881cd0ba4037c3254c) | fix | ensure transitive SCSS partial errors are tracked in watch mode |
+| [8186faa11](https://github.com/angular/angular-cli/commit/8186faa117803ffb6ac8e2c4cd6ab7873502308d) | fix | ensure Vitest mock patching is executed only once |
+| [107d1a9e2](https://github.com/angular/angular-cli/commit/107d1a9e26fc59c7878254e563758818866f0f6e) | fix | preserve error stack traces during prerendering |
+| [b7f457253](https://github.com/angular/angular-cli/commit/b7f4572533675729e87532bdc23509feb2f3a28d) | fix | scope CHROME_BIN executable path to individual playwright instances |
+
+
+
+
+
+# 21.2.5 (2026-03-27)
+
+### @angular/cli
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------- |
+| [cadf9b201](https://github.com/angular/angular-cli/commit/cadf9b201bd1055a6e3cc016eb01e0196b028080) | feat | support custom port in MCP devserver start tool |
+
+### @angular/ssr
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------- |
+| [bbc255419](https://github.com/angular/angular-cli/commit/bbc255419b346e5152391b47f310922f86e9e383) | fix | allow underscores in host validation |
+| [b1fe66a7f](https://github.com/angular/angular-cli/commit/b1fe66a7f8650ce021b4070394bc31848fc64ca5) | fix | patch Headers.forEach in cloneRequestAndPatchHeaders |
+
+
+
+
+
+# 20.3.22 (2026-03-27)
### @angular-devkit/build-angular
| Commit | Type | Description |
| --------------------------------------------------------------------------------------------------- | ---- | ------------------------- |
-| [0e5421ba7](https://github.com/angular/angular-cli/commit/0e5421ba78814cf11e4d4510e930eaacc6458662) | fix | update webpack to 5.105.0 |
+| [5978eeeff](https://github.com/angular/angular-cli/commit/5978eeeff63cd62f1515d949eaad0b5e6f7c44cd) | fix | update picomatch to 4.0.4 |
+
+### @angular-devkit/core
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------- |
+| [6e9b92612](https://github.com/angular/angular-cli/commit/6e9b926129a9dd79f01d47b7446411b8963ffb62) | fix | update picomatch to 4.0.4 |
+
+### @angular/build
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------- |
+| [6f209c26d](https://github.com/angular/angular-cli/commit/6f209c26dc5a454acd1cd76f25240c26978fa827) | fix | update picomatch to 4.0.4 |
-
+
+
+# 19.2.23 (2026-03-27)
+
+### @angular/cli
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------- |
+| [67cfbe32f](https://github.com/angular/angular-cli/commit/67cfbe32ff013d7c9763253c8ccd383d8ee2416c) | fix | update picomatch to 4.0.4 |
+
+### @angular-devkit/build-angular
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------- |
+| [771b979e7](https://github.com/angular/angular-cli/commit/771b979e78af079242bea607470b3ad3b6162659) | fix | update picomatch to 4.0.4 |
+
+### @angular-devkit/core
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------- |
+| [de2da4874](https://github.com/angular/angular-cli/commit/de2da4874026bb47d08de828b6b4b44fd7d9a3c4) | fix | update picomatch to 4.0.4 |
+
+### @angular/build
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------- |
+| [27a9ce4a7](https://github.com/angular/angular-cli/commit/27a9ce4a7446db0533e4a7ef64301eeccc5e21e1) | fix | update picomatch to 4.0.4 |
+
+
+
+
+
+# 22.0.0-next.3 (2026-03-26)
+
+### @angular/cli
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------- |
+| [4bbd1bf53](https://github.com/angular/angular-cli/commit/4bbd1bf532fc2d9c36650121a05eae2fb096eb09) | fix | restore console methods after logger completes |
+
+### @schematics/angular
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------- |
+| [f98cc82eb](https://github.com/angular/angular-cli/commit/f98cc82eb0f46986e61b4f94b57dcd36e4eaf215) | feat | rely on strict template default in generated workspaces |
+
+### @angular/build
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------- |
+| [01bd5d064](https://github.com/angular/angular-cli/commit/01bd5d06410719f66fcabb0bfd04c809e7db12f7) | fix | deduplicate and merge coverage excludes with vitest |
+| [355ebe8c5](https://github.com/angular/angular-cli/commit/355ebe8c54b58201c8080543e5e04208599f0947) | fix | prevent reporter duplicates by explicitly overriding Vitest configuration |
+| [1a8376bee](https://github.com/angular/angular-cli/commit/1a8376bee9b7683b84c2ae50d08e50d5c5c7b99a) | fix | remove default for unit-test coverage option |
+| [a203dcf1d](https://github.com/angular/angular-cli/commit/a203dcf1d9a6d544cbae867aa949ab0582e60a88) | fix | warn about performance of test.exclude in vitest configuration |
+| [ec10eb365](https://github.com/angular/angular-cli/commit/ec10eb365261549c2181efa98a494a749444a787) | fix | warn when vitest watch config conflicts with builder |
+
+### @angular/ssr
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------- |
+| [ca6f08c6e](https://github.com/angular/angular-cli/commit/ca6f08c6e59820160f7ae496a9d8775c78ce1a58) | fix | apply forwarded prefix and vary header in accept-language redirects |
+| [3b99ee140](https://github.com/angular/angular-cli/commit/3b99ee140db82c8ddae3f6e48f9fc38eca204a53) | fix | support '\*' in allowedHosts and warn about security risks |
+
+
+
+
+
+# 21.2.4 (2026-03-26)
+
+### @angular/cli
-# 21.2.0-next.2 (2026-02-11)
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------- |
+| [a7787d092](https://github.com/angular/angular-cli/commit/a7787d0925559fe7731034856a872708bcfb78be) | fix | restore console methods after logger completes |
### @angular/build
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------- |
+| [7170599ab](https://github.com/angular/angular-cli/commit/7170599ab237691d9208c410363ef7e4ee50db2c) | fix | deduplicate and merge coverage excludes with vitest |
+| [c73f13797](https://github.com/angular/angular-cli/commit/c73f13797afe57fcc98faf6361085e1dd5afae9b) | fix | prevent reporter duplicates by explicitly overriding Vitest configuration |
+| [956ccaa71](https://github.com/angular/angular-cli/commit/956ccaa71ea8a3626e4139cf7e2f26ee637feeed) | fix | remove default for unit-test coverage option |
+| [36978db7e](https://github.com/angular/angular-cli/commit/36978db7e494e4e5612aa2a8384199eeca7c4c2d) | fix | warn about performance of test.exclude in vitest configuration |
+| [6ec36f5be](https://github.com/angular/angular-cli/commit/6ec36f5bee05d97c10ca8d91d5746621ffb1fdb9) | fix | warn when vitest watch config conflicts with builder |
+
+### @angular/ssr
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------- |
+| [9bdf782c8](https://github.com/angular/angular-cli/commit/9bdf782c838ab5820ec905d689a62ffc3b3cabe3) | fix | apply forwarded prefix and vary header in accept-language redirects |
+| [628c58672](https://github.com/angular/angular-cli/commit/628c586728748e1c367fa7e363299eb79b1566ca) | fix | support '\*' in allowedHosts and warn about security risks |
+
+
+
+
+
+# 20.3.21 (2026-03-19)
+
+### @angular/ssr
+
| Commit | Type | Description |
| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------- |
-| [cad7a7c0f](https://github.com/angular/angular-cli/commit/cad7a7c0ff3778f04820a99ad0aa9d74f1067fd5) | feat | run vitest browser with playwright with OS theme |
-| [8ae7f59e6](https://github.com/angular/angular-cli/commit/8ae7f59e6f988489fda8c1346e3d2c3768d7a5f0) | fix | correctly resolve absolute setup file paths in Vitest |
-| [fd5cb28c8](https://github.com/angular/angular-cli/commit/fd5cb28c8082417288a896b89bde659bb0dc92e2) | fix | explicitly fail when using Vitest runtime mocking |
+| [1dc6992a5](https://github.com/angular/angular-cli/commit/1dc6992a5ae6c5a1f16f22f6c94690d5cf218c38) | fix | disallow x-forwarded-prefix starting with a backslash |
+| [0a2ff0b2b](https://github.com/angular/angular-cli/commit/0a2ff0b2b3aceb228c9447c19fb762df742d7265) | fix | ensure unique values in redirect response Vary header |
+| [cdbac82a8](https://github.com/angular/angular-cli/commit/cdbac82a85b35f24c70a062eeb8a13b521831019) | fix | support custom headers in redirect responses |
-
+
-# 21.1.4 (2026-02-11)
+# 22.0.0-next.2 (2026-03-18)
+
+### @angular/cli
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------ |
+| [c9f07db8f](https://github.com/angular/angular-cli/commit/c9f07db8fcb0b3f8400fbbf0e131be7f9857a987) | fix | use parsed package name for migrate-only updates |
+
+### @schematics/angular
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------- |
+| [6572a6944](https://github.com/angular/angular-cli/commit/6572a69443356ff0022e6ce162915125fee0e3bb) | fix | default components to OnPush change detection |
### @angular/build
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------ |
+| [9b33e1781](https://github.com/angular/angular-cli/commit/9b33e1781328d3b42665145bf580fb8e06c8ad2b) | fix | alias createRequire banner import to avoid duplicate binding |
+| [4643a8a3b](https://github.com/angular/angular-cli/commit/4643a8a3b3e2a3bcf7baae9f812ae8a9ef10ebad) | fix | only use external packages for polyfills when no local files are present |
+
+### @angular/ssr
+
| Commit | Type | Description |
| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------- |
-| [7a9dd6b47](https://github.com/angular/angular-cli/commit/7a9dd6b47e2191862c64355b10abaeead189759f) | fix | correctly resolve absolute setup file paths in Vitest |
+| [4d564f66f](https://github.com/angular/angular-cli/commit/4d564f66f694011724d4933bf025f1c4fed55bca) | fix | disallow x-forwarded-prefix starting with a backslash |
+| [ff1160e30](https://github.com/angular/angular-cli/commit/ff1160e3015eb321971e398cbedff80f4c053166) | fix | ensure unique values in redirect response Vary header |
+| [998b8298e](https://github.com/angular/angular-cli/commit/998b8298e3106c95d77ced8090ab815365c462c7) | fix | support custom headers in redirect responses |
-
+
-# 20.3.16 (2026-02-09)
+# 21.2.3 (2026-03-18)
### @angular/cli
-| Commit | Type | Description |
-| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------ |
-| [656888a25](https://github.com/angular/angular-cli/commit/656888a250af060c110ae87024b0e475b079c23d) | fix | update dependency @modelcontextprotocol/sdk to v1.26.0 |
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------ |
+| [1505164bb](https://github.com/angular/angular-cli/commit/1505164bb2703254a2b25a76c7b3a1ff2fd76a85) | fix | use parsed package name for migrate-only updates |
+
+### @angular/build
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------ |
+| [75fa94cad](https://github.com/angular/angular-cli/commit/75fa94cad26b0947e687ef94d50653cb7651d18c) | fix | alias createRequire banner import to avoid duplicate binding |
+| [d009aa1ec](https://github.com/angular/angular-cli/commit/d009aa1ec7411b67b61b81003eb6181cde6f306f) | fix | only use external packages for polyfills when no local files are present |
+
+### @angular/ssr
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------- |
+| [f3e0e82c2](https://github.com/angular/angular-cli/commit/f3e0e82c2cecc3d9ebb5b8acc6e64d2d88c4efbd) | fix | disallow x-forwarded-prefix starting with a backslash |
+| [b8bcd59b4](https://github.com/angular/angular-cli/commit/b8bcd59b40496369a57de0b0b39d85f323af30c7) | fix | ensure unique values in redirect response Vary header |
+| [84385411d](https://github.com/angular/angular-cli/commit/84385411d4542d60d635aea9063c1fd751deb607) | fix | support custom headers in redirect responses |
-
+
-# 21.2.0-next.1 (2026-02-05)
+# 22.0.0-next.1 (2026-03-11)
-### @angular/cli
+## Breaking Changes
-| Commit | Type | Description |
-| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------------- |
-| [91b9d281f](https://github.com/angular/angular-cli/commit/91b9d281fc88a242aa6e5dd5495e275990d926ef) | feat | integrate file formatting into update migrations |
-| [6f29a8c35](https://github.com/angular/angular-cli/commit/6f29a8c35abb8928d4e7ea01958192dd2a83491d) | fix | renamed files by their new path in the schematic workflow |
-| [bc363af8b](https://github.com/angular/angular-cli/commit/bc363af8bc40f117a4e35ec9eb7eedf69f5b5b37) | perf | optimize package manager discovery with stat-based probing |
+### @angular/build
+
+- The `@angular/build:dev-server (ng serve)` now assigns the highest priority to the `PORT` environment variable. This value will override any port configurations specified in `angular.json` or via the `--port` command-line flag. This includes the default port 4200.
### @schematics/angular
-| Commit | Type | Description |
-| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------------------- |
-| [5d1df50d8](https://github.com/angular/angular-cli/commit/5d1df50d8b84b453570ae5fd9ab6f949bbc11649) | fix | add actionable feedback to vitest-browser schematic |
-| [51fc77828](https://github.com/angular/angular-cli/commit/51fc77828a33fdf35051b7e18d79ad43f90cba1d) | fix | warn when production configuration is missing for service worker |
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------- |
+| [b3d838dfd](https://github.com/angular/angular-cli/commit/b3d838dfdb2adc3bd035b495f7f9457d742d73a4) | fix | replace deprecated `ChangeDetectionStrategy.Default` with `Eager` |
+
+### @angular/cli
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------------------- |
+| [598a690a0](https://github.com/angular/angular-cli/commit/598a690a0baea5cd54af5cea38e673c33605a627) | fix | conditionally quote package names when adding dependencies based on host requirements |
+| [b5fb457e1](https://github.com/angular/angular-cli/commit/b5fb457e157fa78b61565eaba6b88e9f80b3b288) | fix | preserve exact version in ng add when requested |
+| [93c3eb8fb](https://github.com/angular/angular-cli/commit/93c3eb8fb2a0d531f18779152b0a62e9b73dbb23) | fix | update zoneless migration tool to handle `ChangeDetectionStrategy.Eager` |
+| [ad0fd5f41](https://github.com/angular/angular-cli/commit/ad0fd5f41fc6ee5d920fb3c725f09f17d86d2ab4) | perf | avoid redundant package version resolution in ng add |
+| [a39a33128](https://github.com/angular/angular-cli/commit/a39a33128fb56e9c65ea89e06c4f127252d3b220) | perf | cache root manifest and resolve restricted package exports in ng add |
### @angular/build
-| Commit | Type | Description |
-| --------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------- |
-| [ece30f235](https://github.com/angular/angular-cli/commit/ece30f2359c2dc794b0c9272447f623a121e88b0) | feat | add headless option to unit-test builder |
-| [1f114a9e8](https://github.com/angular/angular-cli/commit/1f114a9e8b9bddd53e01016a2d7cb211a04eee48) | fix | bundle setup files in unit-test builder for Vitest |
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------ |
+| [fe720cab6](https://github.com/angular/angular-cli/commit/fe720cab64bbc8bcc2db583188e32ad938e63a23) | feat | add process.env.PORT support to the dev server |
+| [af2c7e944](https://github.com/angular/angular-cli/commit/af2c7e9444fba81d3b1fd2d37dc4412f8305b5ed) | feat | rename `experimentalPlatform` to `platform` in application builder |
+| [6324133c2](https://github.com/angular/angular-cli/commit/6324133c282f5e04ee6e9e46fc5f387cbbefad8e) | fix | normalize line endings for CSP hash generation |
+| [839c725c2](https://github.com/angular/angular-cli/commit/839c725c234b2c1c3e44d52e3a1442ad1b538be9) | fix | pass process environment variables to prerender workers |
+| [f30f8900e](https://github.com/angular/angular-cli/commit/f30f8900efb8ad9a835630f57e3667346926dc4d) | fix | resolve assets correctly during i18n prerendering |
-
+
-# 21.1.3 (2026-02-05)
+# 21.2.2 (2026-03-11)
-### @schematics/angular
+### @angular/cli
-| Commit | Type | Description |
-| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------------------- |
-| [a18196a10](https://github.com/angular/angular-cli/commit/a18196a1096e5eb69cf64102943781d34c4389bf) | fix | warn when production configuration is missing for service worker |
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------------------- |
+| [8447d9132](https://github.com/angular/angular-cli/commit/8447d913280a8fa09a842d11193ce77527d0f7a6) | fix | conditionally quote package names when adding dependencies based on host requirements |
+| [d2f209823](https://github.com/angular/angular-cli/commit/d2f209823a524a6effde4910017547675c7a6166) | fix | preserve exact version in ng add when requested |
+| [28f4d684a](https://github.com/angular/angular-cli/commit/28f4d684ae12f0e0860bf0ace8851fdddad1c068) | perf | avoid redundant package version resolution in ng add |
-### @angular-devkit/build-angular
+### @angular/build
-| Commit | Type | Description |
-| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------- |
-| [6d05d27ca](https://github.com/angular/angular-cli/commit/6d05d27ca097b16efb139bcee1c45b1b51dfe746) | fix | address Node.js deprecation DEP0190 |
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------- |
+| [06010294f](https://github.com/angular/angular-cli/commit/06010294f8fe7a4843f802aafba51703ce810f61) | fix | allow any `CHROME_BIN` for vitest playwright provider |
+| [8dec0c62b](https://github.com/angular/angular-cli/commit/8dec0c62ba40af339f4fd0fa34f20cbed545cd71) | fix | normalize line endings for CSP hash generation |
+| [58688ebd7](https://github.com/angular/angular-cli/commit/58688ebd727fe295adcb538a33b525867caf82bd) | fix | pass process environment variables to prerender workers |
+| [4ca61647f](https://github.com/angular/angular-cli/commit/4ca61647f208ec0ab9bc06f64583696b0619c259) | fix | resolve assets correctly during i18n prerendering |
+
+
+
+
+
+# 20.3.20 (2026-03-11)
+
+### @angular/build
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------- |
+| [0fd6823af](https://github.com/angular/angular-cli/commit/0fd6823af0adec23f7c3f1d531f45f6432afe555) | fix | pass process environment variables to prerender workers |
-
+
+
+# 22.0.0-next.0 (2026-03-05)
-# 21.2.0-next.0 (2026-01-28)
+## Breaking Changes
+
+###
+
+- Node.js v20 is no longer supported. The minimum supported Node.js versions are now v22.22.0 and v24.13.1.
+- The `@angular-devkit/architect-cli` package is no longer available. The `architect` CLI tool has been moved to the `@angular-devkit/architect` package.
+- The experimental `@angular-devkit/build-angular:jest` and `@angular-devkit/build-angular:web-test-runner` builders have been removed.
+
+### @angular/ssr
+
+- The server no longer falls back to Client-Side Rendering (CSR) when a request fails host validation. Requests with unrecognized 'Host' headers will now return a 400 Bad Request status code. Users must ensure all valid hosts are correctly configured in the 'allowedHosts' option.
### @angular/cli
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------------- |
+| [a5c7c0b5f](https://github.com/angular/angular-cli/commit/a5c7c0b5fda4ae0c00351ba34e5f39dab2c6baee) | fix | reflect new minimum supported Node version in ng.js |
+
+###
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | -------- | ----------------------------------------------------- |
+| [d0e9e8163](https://github.com/angular/angular-cli/commit/d0e9e81639be33c453f1ef66c7c1c8ea4fe543c2) | build | update minimum supported Node.js versions |
+| [1f21e89d9](https://github.com/angular/angular-cli/commit/1f21e89d99c191642627df6842402644a1bf26ee) | refactor | remove `@angular-devkit/architect-cli` package |
+| [b4885b851](https://github.com/angular/angular-cli/commit/b4885b851226709ae4146070122806e14a3d5eb9) | refactor | remove experimental Jest and Web Test Runner builders |
+
+### @angular/build
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------ |
+| [414320d02](https://github.com/angular/angular-cli/commit/414320d02a090b7b36720051569cba7563bf9ac6) | feat | support runtime Zone.js detection in Vitest unit test runner |
+| [f190263a4](https://github.com/angular/angular-cli/commit/f190263a4010a4f7b9ae85806b5d70a16b57b9fa) | fix | allow any `CHROME_BIN` for vitest playwright provider |
+
+### @angular/ssr
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | -------- | ------------------------------------- |
+| [27cd35561](https://github.com/angular/angular-cli/commit/27cd355619aad140dfc221c6bd161f4a981e0f3b) | refactor | remove CSR fallback for invalid hosts |
+
+
+
+
+
+# 21.2.1 (2026-03-05)
+
+### @angular/cli
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------ |
+| [ae4c28d00](https://github.com/angular/angular-cli/commit/ae4c28d0083d948489f4ba38c571b7f955400226) | fix | correct dev dependency detection logic in `ng add` |
+| [465073bc1](https://github.com/angular/angular-cli/commit/465073bc1b2b0e9fa594698651a9e0afe747a74a) | fix | disable npm update notifier in package manager host |
+| [36270634f](https://github.com/angular/angular-cli/commit/36270634f6ff5ab15896a8c2b345659511a8a276) | fix | ensure group members are updated to targeted version |
+| [d87dba6af](https://github.com/angular/angular-cli/commit/d87dba6af1116de0838d8683cd69fd31ed9811fd) | fix | ignore unknown files when formatting schematic changes |
+
+### @schematics/angular
+
| Commit | Type | Description |
| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------------------------- |
-| [0dd04f289](https://github.com/angular/angular-cli/commit/0dd04f289e555a4a8af7bdadabe300da74701e3b) | feat | add markdown files to Prettier's formatting list |
-| [fbae1b6ab](https://github.com/angular/angular-cli/commit/fbae1b6ab384186ae69e804c54815cea80e6a600) | feat | automatic formatting files modified by schematics |
-| [98a24d040](https://github.com/angular/angular-cli/commit/98a24d0401f36f484dc9c4d8b0f5284ffa524f19) | feat | standardize MCP tools around workspace/project options |
-| [d9cd609c5](https://github.com/angular/angular-cli/commit/d9cd609c5d13fe492b1f31973d9be518f8529387) | fix | correctly parse scoped packages in yarn classic list output |
-| [5b05f2500](https://github.com/angular/angular-cli/commit/5b05f25005621828565585692b1d7a67c5f0fec8) | fix | enable shell option for Prettier execution on Windows platforms |
+| [72d466aa0](https://github.com/angular/angular-cli/commit/72d466aa04d4d0cc4d654410bcb6dd44f0de3357) | fix | prevent adding test dependencies when minimal option is enabled |
+
+### @angular-devkit/build-angular
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------- |
+| [0019d1c8e](https://github.com/angular/angular-cli/commit/0019d1c8e1494295a754063dbf936e1cd40d05bd) | fix | update copy-webpack-plugin to v14.0.0 |
+
+### @angular/build
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------------ |
+| [6ad860863](https://github.com/angular/angular-cli/commit/6ad8608636ad48ae140cc7299a32e0358c761fcc) | fix | bundle polyfills to preserve execution order in dev server |
+| [d17397375](https://github.com/angular/angular-cli/commit/d1739737564fbcc3e4c5a6c3369046cccf0f6120) | fix | conditionally allow `vi.mock` for non-relative imports |
+| [0d49f86ed](https://github.com/angular/angular-cli/commit/0d49f86edf5592f0266c6d6689ab4d55b27b2d8d) | fix | resolve style include paths relative to `ng-package.json` in unit-test builder |
+| [584f6a2d9](https://github.com/angular/angular-cli/commit/584f6a2d95ac4bdd9f20d918c6700ea79227cc92) | fix | treat empty browsers array as undefined in unit-test builder |
+| [6699cdc9b](https://github.com/angular/angular-cli/commit/6699cdc9bfbabc3de2ff0cf03acfd6989dc5596c) | perf | fix memory leak in `ng serve` with i18n |
+
+### @angular/ssr
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------- |
+| [43a9dfa66](https://github.com/angular/angular-cli/commit/43a9dfa663c386217c9a654f0e80af74823fcf6a) | fix | improve header validation logic |
+| [dee3717b3](https://github.com/angular/angular-cli/commit/dee3717b3faae9ea75d0a5e53c925f915949b8d0) | fix | introduce DI token to signal route discovery process |
+
+
+
+
+
+# 20.3.19 (2026-03-04)
+
+### @angular-devkit/build-angular
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------- |
+| [0299b4d1a](https://github.com/angular/angular-cli/commit/0299b4d1aca13f11a06e2e92c593fe3e20906d23) | fix | update copy-webpack-plugin to v14.0.0 |
+
+
+
+
+
+# 20.3.18 (2026-02-26)
+
+### @angular-devkit/core
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------ |
+| [39596d529](https://github.com/angular/angular-cli/commit/39596d529f831f72a2134bc3c9ac163867ff5702) | fix | update `ajv` to `8.18.0` |
+
+### @angular/build
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ----------------------- |
+| [f668e2778](https://github.com/angular/angular-cli/commit/f668e2778c4c4dbecc8a1c6831c092f5512d1ec1) | fix | update rollup to 4.59.0 |
+
+
+
+
+
+# 19.2.22 (2026-02-26)
+
+### @angular-devkit/core
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------ |
+| [0a01aecd9](https://github.com/angular/angular-cli/commit/0a01aecd92a5dfa1a254429481906a88be725c5e) | fix | update `ajv` to `8.18.0` |
+
+### @angular/build
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ----------------------- |
+| [79f59412a](https://github.com/angular/angular-cli/commit/79f59412a17eeabd2ad5df1d614ed634354d1eee) | fix | update rollup to 4.59.0 |
+
+
+
+
+
+# 21.2.0 (2026-02-25)
+
+### @angular/cli
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------- |
+| [0dd04f289](https://github.com/angular/angular-cli/commit/0dd04f289e555a4a8af7bdadabe300da74701e3b) | feat | add markdown files to Prettier's formatting list |
+| [fbae1b6ab](https://github.com/angular/angular-cli/commit/fbae1b6ab384186ae69e804c54815cea80e6a600) | feat | automatic formatting files modified by schematics |
+| [91b9d281f](https://github.com/angular/angular-cli/commit/91b9d281fc88a242aa6e5dd5495e275990d926ef) | feat | integrate file formatting into update migrations |
+| [98a24d040](https://github.com/angular/angular-cli/commit/98a24d0401f36f484dc9c4d8b0f5284ffa524f19) | feat | standardize MCP tools around workspace/project options |
+| [d9cd609c5](https://github.com/angular/angular-cli/commit/d9cd609c5d13fe492b1f31973d9be518f8529387) | fix | correctly parse scoped packages in yarn classic list output |
+| [5b05f2500](https://github.com/angular/angular-cli/commit/5b05f25005621828565585692b1d7a67c5f0fec8) | fix | enable shell option for Prettier execution on Windows platforms |
+| [25b8a157d](https://github.com/angular/angular-cli/commit/25b8a157df70fb0d2c4e6c5438a50ec12e3abc0c) | fix | quote complex range specifiers in package manager |
+| [6f29a8c35](https://github.com/angular/angular-cli/commit/6f29a8c35abb8928d4e7ea01958192dd2a83491d) | fix | renamed files by their new path in the schematic workflow |
+| [201a036f2](https://github.com/angular/angular-cli/commit/201a036f204a6940f70a36a507a4a53d144b5768) | fix | simplify Angular version compatibility checks and add special handling for local builds of new major versions |
+| [cdd26bb66](https://github.com/angular/angular-cli/commit/cdd26bb66d8ab334f76323c2b5cae1aa8ce815f6) | fix | validate package manager version using `semver.valid` and throw an error if invalid |
+| [bc363af8b](https://github.com/angular/angular-cli/commit/bc363af8bc40f117a4e35ec9eb7eedf69f5b5b37) | perf | optimize package manager discovery with stat-based probing |
### @schematics/angular
@@ -114,18 +573,112 @@
| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------------------------- |
| [aa7381efd](https://github.com/angular/angular-cli/commit/aa7381efd213eff70a8004731a7e2b06a60cb8c2) | feat | add a '.prettierrc' file to generated workspaces and add Prettier as dev dependency |
| [f80db6fb7](https://github.com/angular/angular-cli/commit/f80db6fb714aa326f6ed03a8a51090ca59ad0955) | feat | add ng-add support for Vitest browser providers |
-
-### @angular-devkit/build-angular
-
-| Commit | Type | Description |
-| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------- |
-| [b4a8d198c](https://github.com/angular/angular-cli/commit/b4a8d198c78aaf0cac7671f26162ce5818a5704c) | fix | address Node.js deprecation DEP0190 |
+| [5d1df50d8](https://github.com/angular/angular-cli/commit/5d1df50d8b84b453570ae5fd9ab6f949bbc11649) | fix | add actionable feedback to vitest-browser schematic |
### @angular/build
| Commit | Type | Description |
| --------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------------- |
+| [ece30f235](https://github.com/angular/angular-cli/commit/ece30f2359c2dc794b0c9272447f623a121e88b0) | feat | add headless option to unit-test builder |
+| [cad7a7c0f](https://github.com/angular/angular-cli/commit/cad7a7c0ff3778f04820a99ad0aa9d74f1067fd5) | feat | run vitest browser with playwright with OS theme |
| [0b4982720](https://github.com/angular/angular-cli/commit/0b4982720e111bf5029bcf97f7e0ce2658c42d43) | fix | adjust sourcemap sources when Vitest wrapper is bypassed |
+| [1f114a9e8](https://github.com/angular/angular-cli/commit/1f114a9e8b9bddd53e01016a2d7cb211a04eee48) | fix | bundle setup files in unit-test builder for Vitest |
+| [fd5cb28c8](https://github.com/angular/angular-cli/commit/fd5cb28c8082417288a896b89bde659bb0dc92e2) | fix | explicitly fail when using Vitest runtime mocking |
+| [dc899e8a5](https://github.com/angular/angular-cli/commit/dc899e8a530979de8e9579f2281b681e6f737a62) | fix | normalize `allowedHosts` in dev-server |
+| [26bbea12f](https://github.com/angular/angular-cli/commit/26bbea12f872c18e59de05d3c51cc11dd0a09cda) | fix | serve extensionless assets without transformation |
+
+
+
+
+
+# 21.1.5 (2026-02-23)
+
+### @angular/ssr
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------------- |
+| [8695d6063](https://github.com/angular/angular-cli/commit/8695d6063e4c4123a37a3450c7db3a7c9d99050a) | fix | prevent open redirect via X-Forwarded-Prefix header |
+| [e4d445ec6](https://github.com/angular/angular-cli/commit/e4d445ec674841dd1d374c83344c0a704ee3807c) | fix | validate host headers to prevent header-based SSRF |
+
+
+
+
+
+# 20.3.17 (2026-02-23)
+
+### @angular/ssr
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------------- |
+| [8700e18d7](https://github.com/angular/angular-cli/commit/8700e18d7cf175d80fe6ce6205589767b7870c1c) | fix | prevent open redirect via X-Forwarded-Prefix header |
+| [67582a946](https://github.com/angular/angular-cli/commit/67582a946808d2c021cbcfacbf203ef58a6fbded) | fix | validate host headers to prevent header-based SSRF |
+
+
+
+
+
+# 19.2.21 (2026-02-23)
+
+### @angular/ssr
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------------- |
+| [288e22816](https://github.com/angular/angular-cli/commit/288e228161e3c0cceb392dd5fc24d07b129a3a64) | fix | prevent open redirect via X-Forwarded-Prefix header |
+| [2a72d7483](https://github.com/angular/angular-cli/commit/2a72d7483d87ccdcfa0c5148f34a4c6ebb6c6cf9) | fix | validate host headers to prevent header-based SSRF |
+
+
+
+
+
+# 19.2.20 (2026-02-13)
+
+### @angular-devkit/build-angular
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------- |
+| [0e5421ba7](https://github.com/angular/angular-cli/commit/0e5421ba78814cf11e4d4510e930eaacc6458662) | fix | update webpack to 5.105.0 |
+
+
+
+
+
+# 21.1.4 (2026-02-11)
+
+### @angular/build
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------- |
+| [7a9dd6b47](https://github.com/angular/angular-cli/commit/7a9dd6b47e2191862c64355b10abaeead189759f) | fix | correctly resolve absolute setup file paths in Vitest |
+
+
+
+
+
+# 20.3.16 (2026-02-09)
+
+### @angular/cli
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------ |
+| [656888a25](https://github.com/angular/angular-cli/commit/656888a250af060c110ae87024b0e475b079c23d) | fix | update dependency @modelcontextprotocol/sdk to v1.26.0 |
+
+
+
+
+
+# 21.1.3 (2026-02-05)
+
+### @schematics/angular
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------------------- |
+| [a18196a10](https://github.com/angular/angular-cli/commit/a18196a1096e5eb69cf64102943781d34c4389bf) | fix | warn when production configuration is missing for service worker |
+
+### @angular-devkit/build-angular
+
+| Commit | Type | Description |
+| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------- |
+| [6d05d27ca](https://github.com/angular/angular-cli/commit/6d05d27ca097b16efb139bcee1c45b1b51dfe746) | fix | address Node.js deprecation DEP0190 |
@@ -2401,7 +2954,6 @@
- Protractor is no longer supported.
Protractor was marked end-of-life in August 2023 (see https://protractortest.org/). Projects still relying on Protractor should consider migrating to another E2E testing framework, several support solid migration paths from Protractor.
-
- https://angular.dev/tools/cli/end-to-end
- https://blog.angular.dev/the-state-of-end-to-end-testing-with-angular-d175f751cb9c
@@ -6036,7 +6588,6 @@ Alan Agius, Charles Lyding and Doug Parker
### @angular/cli
- Several changes to the `ng analytics` command syntax.
-
- `ng analytics project ` has been replaced with `ng analytics `
- `ng analytics ` has been replaced with `ng analytics --global`
@@ -6066,7 +6617,6 @@ Alan Agius, Charles Lyding and Doug Parker
- `browser` and `karma` builders `script` and `styles` options input files extensions are now validated.
Valid extensions for `scripts` are:
-
- `.js`
- `.cjs`
- `.mjs`
@@ -6075,7 +6625,6 @@ Alan Agius, Charles Lyding and Doug Parker
- `.mjsx`
Valid extensions for `styles` are:
-
- `.css`
- `.less`
- `.sass`
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 06db9756c89d..8b57f53f5584 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -12,7 +12,7 @@ to follow:
- [Coding Rules](#rules)
- [Commit Message Guidelines](#commit)
- [Signing the CLA](#cla)
- - [Updating the Public API](#public-api)
+ - [Spam Policy](#spam-policy)
## Code of Conduct
Help us keep Angular open and inclusive. Please read and follow our [Code of Conduct][coc].
@@ -217,7 +217,6 @@ The following is the list of supported scopes:
* **@angular/pwa**
* **@angular/ssr**
* **@angular-devkit/architect**
-* **@angular-devkit/architect-cli**
* **@angular-devkit/build-angular**
* **@angular-devkit/build-webpack**
* **@angular-devkit/core**
@@ -290,6 +289,9 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
* For corporations we'll need you to
[print, sign and one of scan+email, fax or mail the form][corporate-cla].
+## Spam Policy
+
+See [Spam Policy](https://github.com/angular/angular/blob/main/contributing-docs/spam.md) for details.
[coc]: https://github.com/angular/code-of-conduct/blob/main/CODE_OF_CONDUCT.md
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
diff --git a/MODULE.bazel b/MODULE.bazel
index e218349c3d69..034b10ad3e91 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -5,56 +5,57 @@ module(
)
bazel_dep(name = "platforms", version = "1.0.0")
-bazel_dep(name = "yq.bzl", version = "0.3.4")
+bazel_dep(name = "yq.bzl", version = "0.3.5")
bazel_dep(name = "rules_nodejs", version = "6.7.3")
-bazel_dep(name = "aspect_rules_js", version = "2.9.2")
-bazel_dep(name = "aspect_rules_ts", version = "3.8.4")
+bazel_dep(name = "aspect_rules_js", version = "3.0.3")
+bazel_dep(name = "aspect_rules_ts", version = "3.8.8")
bazel_dep(name = "rules_pkg", version = "1.2.0")
-bazel_dep(name = "rules_cc", version = "0.2.16")
-bazel_dep(name = "aspect_bazel_lib", version = "2.22.5")
+bazel_dep(name = "rules_cc", version = "0.2.17")
+bazel_dep(name = "jq.bzl", version = "0.6.1")
+bazel_dep(name = "bazel_lib", version = "3.2.2")
bazel_dep(name = "bazel_skylib", version = "1.9.0")
-bazel_dep(name = "aspect_rules_esbuild", version = "0.25.0")
-bazel_dep(name = "aspect_rules_jasmine", version = "2.0.2")
+bazel_dep(name = "aspect_rules_esbuild", version = "0.25.1")
+bazel_dep(name = "aspect_rules_jasmine", version = "2.0.4")
bazel_dep(name = "rules_angular")
git_override(
module_name = "rules_angular",
- commit = "d746c4f75e42cffe389d1ab077f4639be2bc78d1",
- remote = "https://github.com/devversion/rules_angular.git",
+ commit = "6c36180c2efebc6526ef0e6a55a6d738c7de6909",
+ remote = "https://github.com/angular/rules_angular.git",
)
bazel_dep(name = "devinfra")
git_override(
module_name = "devinfra",
- commit = "469708f109a90884ca403d150d33079a3a5a8769",
+ commit = "ba726e7bca0b08b125ccc6f93c233749e1213c17",
remote = "https://github.com/angular/dev-infra.git",
)
bazel_dep(name = "rules_sass")
git_override(
module_name = "rules_sass",
- commit = "1184a80751a21af8348f308abc5b38a41f26850e",
- remote = "https://github.com/devversion/rules_sass.git",
+ commit = "b5ddaa8e77509bcce35158ad20009636d3da4fbc",
+ remote = "https://github.com/angular/rules_sass.git",
)
bazel_dep(name = "rules_browsers")
git_override(
module_name = "rules_browsers",
- commit = "e08ae33c679d07b3b2fcc136658b787a81995bc5",
- remote = "https://github.com/devversion/rules_browsers.git",
+ commit = "652b57c41218be318f33fc92032696f53d3aa0ef",
+ remote = "https://github.com/angular/rules_browsers.git",
)
node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node")
node.toolchain(
node_repositories = {
- "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"),
+ "22.22.2-darwin_arm64": ("node-v22.22.2-darwin-arm64.tar.gz", "node-v22.22.2-darwin-arm64", "db4b275b83736df67533529a18cc55de2549a8329ace6c7bcc68f8d22d3c9000"),
+ "22.22.2-darwin_amd64": ("node-v22.22.2-darwin-x64.tar.gz", "node-v22.22.2-darwin-x64", "12a6abb9c2902cf48a21120da13f87fde1ed1b71a13330712949e8db818708ba"),
+ "22.22.2-linux_arm64": ("node-v22.22.2-linux-arm64.tar.xz", "node-v22.22.2-linux-arm64", "e9e1930fd321a470e29bb68f30318bf58e3ecb4acb4f1533fb19c58328a091fe"),
+ "22.22.2-linux_ppc64le": ("node-v22.22.2-linux-ppc64le.tar.xz", "node-v22.22.2-linux-ppc64le", "14045b5a5030d35ca0030fb7e870bd11a651eb9b57323ebc0021e8d78ac6bac9"),
+ "22.22.2-linux_s390x": ("node-v22.22.2-linux-s390x.tar.xz", "node-v22.22.2-linux-s390x", "9e4a07c291b8949289c6ea8ee61b1d14666a4810feae776a8d1eb1f57e03a2fb"),
+ "22.22.2-linux_amd64": ("node-v22.22.2-linux-x64.tar.xz", "node-v22.22.2-linux-x64", "88fd1ce767091fd8d4a99fdb2356e98c819f93f3b1f8663853a2dee9b438068a"),
+ "22.22.2-windows_amd64": ("node-v22.22.2-win-x64.zip", "node-v22.22.2-win-x64", "7c93e9d92bf68c07182b471aa187e35ee6cd08ef0f24ab060dfff605fcc1c57c"),
},
- node_version = "22.22.0",
+ node_version = "22.22.2",
)
use_repo(
node,
@@ -68,31 +69,37 @@ use_repo(
node_dev = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node", dev_dependency = True)
-# Node.js 20
-node_dev.toolchain(
- name = "node20",
- node_version = "20.19.0",
-)
-
# Node.js 22
node_dev.toolchain(
name = "node22",
- node_version = "22.12.0",
+ node_repositories = {
+ "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.0",
)
# Node.js 24
node_dev.toolchain(
name = "node24",
- node_version = "24.0.0",
+ node_repositories = {
+ "24.13.1-darwin_arm64": ("node-v24.13.1-darwin-arm64.tar.gz", "node-v24.13.1-darwin-arm64", "8c039d59f2fec6195e4281ad5b0d02b9a940897b4df7b849c6fb48be6787bba6"),
+ "24.13.1-darwin_amd64": ("node-v24.13.1-darwin-x64.tar.gz", "node-v24.13.1-darwin-x64", "527f0578d9812e7dfa225121bda0b1546a6a0e4b5f556295fc8299c272de5fbf"),
+ "24.13.1-linux_arm64": ("node-v24.13.1-linux-arm64.tar.xz", "node-v24.13.1-linux-arm64", "c827d3d301e2eed1a51f36d0116b71b9e3d9e3b728f081615270ea40faac34c1"),
+ "24.13.1-linux_ppc64le": ("node-v24.13.1-linux-ppc64le.tar.xz", "node-v24.13.1-linux-ppc64le", "fb712a08d317655dbf776c90f60ac2105109d802e33811df6c9ed33d12f801c6"),
+ "24.13.1-linux_s390x": ("node-v24.13.1-linux-s390x.tar.xz", "node-v24.13.1-linux-s390x", "8e2c0d9b5545c3db22623e8cb8d6f0c28fcd470f29d32dbeabf9432dda289de2"),
+ "24.13.1-linux_amd64": ("node-v24.13.1-linux-x64.tar.xz", "node-v24.13.1-linux-x64", "30215f90ea3cd04dfbc06e762c021393fa173a1d392974298bbc871a8e461089"),
+ "24.13.1-windows_amd64": ("node-v24.13.1-win-x64.zip", "node-v24.13.1-win-x64", "fba577c4bb87df04d54dd87bbdaa5a2272f1f99a2acbf9152e1a91b8b5f0b279"),
+ },
+ node_version = "24.13.1",
)
use_repo(
node_dev,
- "node20_darwin_amd64",
- "node20_darwin_arm64",
- "node20_linux_amd64",
- "node20_linux_arm64",
- "node20_toolchains",
- "node20_windows_amd64",
"node22_darwin_amd64",
"node22_darwin_arm64",
"node22_linux_amd64",
@@ -110,8 +117,8 @@ use_repo(
pnpm = use_extension("@aspect_rules_js//npm:extensions.bzl", "pnpm")
pnpm.pnpm(
name = "pnpm",
- pnpm_version = "10.29.3",
- pnpm_version_integrity = "sha512-SY4ftMylqgbB3PJhHm+vxQly/+cYmZjECekN50VmREKY/+Q+bNKs3Hdboap8xeCSqLcFTIEbqMV3D4RpPTPS3A==",
+ pnpm_version = "10.33.0",
+ pnpm_version_integrity = "sha512-EFaLtKavtYyes2MNqQzJUWQXq+vT+rvmc58K55VyjaFJHp21pUTHatjrdXD1xLs9bGN7LLQb/c20f6gjyGSTGQ==",
)
use_repo(pnpm, "pnpm")
@@ -132,7 +139,6 @@ npm.npm_translate_lock(
"//packages/angular/pwa:package.json",
"//packages/angular/ssr:package.json",
"//packages/angular_devkit/architect:package.json",
- "//packages/angular_devkit/architect_cli:package.json",
"//packages/angular_devkit/build_angular:package.json",
"//packages/angular_devkit/build_webpack:package.json",
"//packages/angular_devkit/core:package.json",
@@ -155,14 +161,22 @@ npm.npm_translate_lock(
npmrc = "//:.npmrc",
pnpm_lock = "//:pnpm-lock.yaml",
)
+
+# This is needed as by default `.md` files are excluded from the npm package.
+# But @angular/core includes best-practices.md file.
+# See: https://github.com/aspect-build/rules_js/blob/786a74a158dd36ed073188b0e506c423cd05501a/npm/private/exclude_package_contents_presets.bzl#L29
+npm.npm_exclude_package_contents(
+ package = "@angular/core",
+ presets = [],
+)
use_repo(npm, "npm")
rules_ts_ext = use_extension("@aspect_rules_ts//ts:extensions.bzl", "ext")
rules_ts_ext.deps(
name = "angular_cli_npm_typescript",
- # Obtained by: npm info typescript@5.9.3 dist.integrity
- ts_integrity = "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
- ts_version = "5.9.3",
+ # Obtained by: npm info typescript@6.0.2 dist.integrity
+ ts_integrity = "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==",
+ ts_version = "6.0.2",
)
use_repo(rules_ts_ext, **{"npm_typescript": "angular_cli_npm_typescript"})
@@ -180,5 +194,4 @@ register_toolchains(
"@devinfra//bazel/git-toolchain:git_macos_arm64_toolchain",
"@devinfra//bazel/git-toolchain:git_windows_toolchain",
"//tools/toolchains:dummy_cc_windows_no_exec_toolchain",
- "//tools/toolchains:node24_windows_no_exec_toolchain",
)
diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock
index 8af79006afc6..00e91c977301 100644
--- a/MODULE.bazel.lock
+++ b/MODULE.bazel.lock
@@ -18,22 +18,15 @@
"https://bcr.bazel.build/modules/aspect_bazel_lib/2.22.5/source.json": "ac2c3213df8f985785f1d0aeb7f0f73d5324e6e67d593d9b9470fb74a25d4a9b",
"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.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_esbuild/0.25.1/MODULE.bazel": "9b931b3e483bd8eedb6966bda6df07d801f70ccb4896231b4e5e711b5130f3aa",
+ "https://bcr.bazel.build/modules/aspect_rules_esbuild/0.25.1/source.json": "a0b72e23ed06113f3878cb635d586b4045ef37750983467af72fe0315c3a2fcd",
+ "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_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_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.3/MODULE.bazel": "a26c28ebcd0c0d50ab0708ac21fa48bd2dced3a4dad4c31a2fa48588b42ad762",
- "https://bcr.bazel.build/modules/aspect_rules_ts/3.8.4/MODULE.bazel": "a50254ac3add6232d0f9f93103836f9afaf614315589a13abf74183982c4101d",
- "https://bcr.bazel.build/modules/aspect_rules_ts/3.8.4/source.json": "f786e0763f3ea5de7ea6d4c4e38fccb48bf4d9c5eafaf95091c0e1590502510e",
- "https://bcr.bazel.build/modules/aspect_tools_telemetry/0.2.3/MODULE.bazel": "20f53b145f40957a51077ae90b37b7ce83582a1daf9350349f0f86179e19dd0d",
+ "https://bcr.bazel.build/modules/aspect_rules_js/3.0.3/MODULE.bazel": "28a30e8fc33bf64a67835d64d124f6e05a7d59648dcb27b110fb3502f761e503",
+ "https://bcr.bazel.build/modules/aspect_rules_js/3.0.3/source.json": "bb8fff9a304452e1042af9522ad1d54d6f1d1fdf71c5127deadb6fd156654193",
+ "https://bcr.bazel.build/modules/aspect_rules_ts/3.8.8/MODULE.bazel": "b52b929a948438665809d49af610f58d1b14f63d6d21ab748f47b6050be4c1f6",
+ "https://bcr.bazel.build/modules/aspect_rules_ts/3.8.8/source.json": "5414530b761a45ab7ca6c49f0a2a9cf8dc0da772f5037cf05ca18aaa64bb1b19",
"https://bcr.bazel.build/modules/aspect_tools_telemetry/0.2.6/MODULE.bazel": "cafb8781ad591bc57cc765dca5fefab08cf9f65af363d162b79d49205c7f8af7",
"https://bcr.bazel.build/modules/aspect_tools_telemetry/0.2.8/MODULE.bazel": "aa975a83e72bcaac62ee61ab12b788ea324a1d05c4aab28aadb202f647881679",
"https://bcr.bazel.build/modules/aspect_tools_telemetry/0.3.3/MODULE.bazel": "37c764292861c2f70314efa9846bb6dbb44fc0308903b3285da6528305450183",
@@ -49,19 +42,20 @@
"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.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7",
+ "https://bcr.bazel.build/modules/bazel_features/1.41.0/MODULE.bazel": "6e0f87fafed801273c371d41e22a15a6f8abf83fdd7f87d5e44ad317b94433d0",
+ "https://bcr.bazel.build/modules/bazel_features/1.41.0/source.json": "8fd525b31b0883c47e0593443cdd10219b94a7556b3195fc02d75c86c66cfe30",
"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",
- "https://bcr.bazel.build/modules/bazel_lib/3.0.0-beta.1/MODULE.bazel": "407729e232f611c3270005b016b437005daa7b1505826798ea584169a476e878",
+ "https://bcr.bazel.build/modules/bazel_lib/3.0.0-rc.0/MODULE.bazel": "d6e00979a98ac14ada5e31c8794708b41434d461e7e7ca39b59b765e6d233b18",
"https://bcr.bazel.build/modules/bazel_lib/3.0.0/MODULE.bazel": "22b70b80ac89ad3f3772526cd9feee2fa412c2b01933fea7ed13238a448d370d",
- "https://bcr.bazel.build/modules/bazel_lib/3.0.0/source.json": "895f21909c6fba01d7c17914bb6c8e135982275a1b18cdaa4e62272217ef1751",
+ "https://bcr.bazel.build/modules/bazel_lib/3.2.2/MODULE.bazel": "e2c890c8a515d6bca9c66d47718aa9e44b458fde64ec7204b8030bf2d349058c",
+ "https://bcr.bazel.build/modules/bazel_lib/3.2.2/source.json": "9e84e115c20e14652c5c21401ae85ff4daa8702e265b5c0b3bf89353f17aa212",
"https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8",
"https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e",
"https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686",
"https://bcr.bazel.build/modules/bazel_skylib/1.2.1/MODULE.bazel": "f35baf9da0efe45fa3da1696ae906eea3d615ad41e2e3def4aeb4e8bc0ef9a7a",
"https://bcr.bazel.build/modules/bazel_skylib/1.3.0/MODULE.bazel": "20228b92868bf5cfc41bda7afc8a8ba2a543201851de39d990ec957b513579c5",
- "https://bcr.bazel.build/modules/bazel_skylib/1.4.0/MODULE.bazel": "2ab127ef8d56a739a99bb2ce00ec4c7d1ecc7977d4370c0ca6efd0d8f03d6d99",
"https://bcr.bazel.build/modules/bazel_skylib/1.4.1/MODULE.bazel": "a0dcb779424be33100dcae821e9e27e4f2901d9dfd5333efe5ac6a8d7ab75e1d",
"https://bcr.bazel.build/modules/bazel_skylib/1.4.2/MODULE.bazel": "3bd40978e7a1fac911d5989e6b09d8f64921865a45822d8b09e815eaa726a651",
"https://bcr.bazel.build/modules/bazel_skylib/1.5.0/MODULE.bazel": "32880f5e2945ce6a03d1fbd588e9198c0a959bb42297b2cfaf1685b7bc32e138",
@@ -75,14 +69,17 @@
"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.1/source.json": "fa7b512dfcb5eafd90ce3959cf42a2a6fe96144ebbb4b3b3928054895f2afac2",
+ "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/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",
"https://bcr.bazel.build/modules/googletest/1.14.0.bcr.1/source.json": "41e9e129f80d8c8bf103a7acc337b76e54fad1214ac0a7084bf24f4cd924b8b4",
"https://bcr.bazel.build/modules/googletest/1.14.0/MODULE.bazel": "cfbcbf3e6eac06ef9d85900f64424708cc08687d1b527f0ef65aa7517af8118f",
"https://bcr.bazel.build/modules/jq.bzl/0.1.0/MODULE.bazel": "2ce69b1af49952cd4121a9c3055faa679e748ce774c7f1fda9657f936cae902f",
- "https://bcr.bazel.build/modules/jq.bzl/0.1.0/source.json": "746bf13cac0860f091df5e4911d0c593971cd8796b5ad4e809b2f8e133eee3d5",
+ "https://bcr.bazel.build/modules/jq.bzl/0.4.0/MODULE.bazel": "a7b39b37589f2b0dad53fd6c1ccaabbdb290330caa920d7ef3e6aad068cd4ab2",
+ "https://bcr.bazel.build/modules/jq.bzl/0.6.1/MODULE.bazel": "f30c46e0a08a9f7566a8bf60a43d48abea960cd7f57b315b01e2762f1537eb52",
+ "https://bcr.bazel.build/modules/jq.bzl/0.6.1/source.json": "9ca9e2f90baa6a5bb0a49626ed9528554ec83165adf47b39792673ecc7feda22",
"https://bcr.bazel.build/modules/jsoncpp/1.9.5/MODULE.bazel": "31271aedc59e815656f5736f282bb7509a97c7ecb43e927ac1a37966e0578075",
"https://bcr.bazel.build/modules/jsoncpp/1.9.5/source.json": "4108ee5085dd2885a341c7fab149429db457b3169b86eb081fa245eadf69169d",
"https://bcr.bazel.build/modules/libpfm/4.11.0/MODULE.bazel": "45061ff025b301940f1e30d2c16bea596c25b176c8b6b3087e92615adbd52902",
@@ -105,6 +102,7 @@
"https://bcr.bazel.build/modules/protobuf/29.0/MODULE.bazel": "319dc8bf4c679ff87e71b1ccfb5a6e90a6dbc4693501d471f48662ac46d04e4e",
"https://bcr.bazel.build/modules/protobuf/29.0/source.json": "b857f93c796750eef95f0d61ee378f3420d00ee1dd38627b27193aa482f4f981",
"https://bcr.bazel.build/modules/protobuf/3.19.0/MODULE.bazel": "6b5fbb433f760a99a22b18b6850ed5784ef0e9928a72668b66e4d7ccd47db9b0",
+ "https://bcr.bazel.build/modules/protobuf/3.19.6/MODULE.bazel": "9233edc5e1f2ee276a60de3eaa47ac4132302ef9643238f23128fea53ea12858",
"https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e",
"https://bcr.bazel.build/modules/pybind11_bazel/2.11.1/source.json": "be4789e951dd5301282729fe3d4938995dc4c1a81c2ff150afc9f1b0504c6022",
"https://bcr.bazel.build/modules/re2/2023-09-01/MODULE.bazel": "cb3d511531b16cfc78a225a9e2136007a48cf8a677e4264baeab57fe78a80206",
@@ -123,7 +121,9 @@
"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.17/MODULE.bazel": "1849602c86cb60da8613d2de887f9566a6d354a6df6d7009f9d04a14402f9a84",
+ "https://bcr.bazel.build/modules/rules_cc/0.2.17/source.json": "3832f45d145354049137c0090df04629d9c2b5493dc5c2bf46f1834040133a07",
+ "https://bcr.bazel.build/modules/rules_cc/0.2.4/MODULE.bazel": "1ff1223dfd24f3ecf8f028446d4a27608aa43c3f41e346d22838a4223980b8cc",
"https://bcr.bazel.build/modules/rules_foreign_cc/0.9.0/MODULE.bazel": "c9e8c682bf75b0e7c704166d79b599f93b72cfca5ad7477df596947891feeef6",
"https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/MODULE.bazel": "40c97d1144356f52905566c55811f13b299453a14ac7769dfba2ac38192337a8",
"https://bcr.bazel.build/modules/rules_fuzzing/0.5.2/source.json": "c8b1e2c717646f1702290959a3302a178fb639d987ab61d548105019f11e527e",
@@ -157,10 +157,7 @@
"https://bcr.bazel.build/modules/rules_license/1.0.0/MODULE.bazel": "a7fda60eefdf3d8c827262ba499957e4df06f659330bbe6cdbdb975b768bb65c",
"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_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc",
@@ -194,17 +191,18 @@
"https://bcr.bazel.build/modules/stardoc/0.7.1/MODULE.bazel": "3548faea4ee5dda5580f9af150e79d0f6aea934fc60c1cc50f4efdd9420759e7",
"https://bcr.bazel.build/modules/stardoc/0.7.2/MODULE.bazel": "fc152419aa2ea0f51c29583fab1e8c99ddefd5b3778421845606ee628629e0e5",
"https://bcr.bazel.build/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216",
+ "https://bcr.bazel.build/modules/tar.bzl/0.10.1/MODULE.bazel": "bf5fda5b5ccef8c3c4a5f4886144377386e0baa382972f257acb42dcf40ea908",
+ "https://bcr.bazel.build/modules/tar.bzl/0.10.1/source.json": "3f1beb35acf53c270a9de493cdc775a985551d7069cfcf24e136b42f683bbb10",
"https://bcr.bazel.build/modules/tar.bzl/0.2.1/MODULE.bazel": "52d1c00a80a8cc67acbd01649e83d8dd6a9dc426a6c0b754a04fe8c219c76468",
"https://bcr.bazel.build/modules/tar.bzl/0.5.1/MODULE.bazel": "7c2eb3dcfc53b0f3d6f9acdfd911ca803eaf92aadf54f8ca6e4c1f3aee288351",
- "https://bcr.bazel.build/modules/tar.bzl/0.8.1/MODULE.bazel": "6ffe8907ed4c555bc94bd35a5a01411cc4470c6dace84f9cf487815409e077d1",
- "https://bcr.bazel.build/modules/tar.bzl/0.8.1/source.json": "835f83b482facf6205ad8708cf2b2f6524d1d7b1075a90fe9bb540da761d6d2e",
+ "https://bcr.bazel.build/modules/tar.bzl/0.6.0/MODULE.bazel": "a3584b4edcfafcabd9b0ef9819808f05b372957bbdff41601429d5fd0aac2e7c",
"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.4/MODULE.bazel": "d3a270662f5d766cd7229732d65a5a5bc485240c3007343dd279edfb60c9ae27",
- "https://bcr.bazel.build/modules/yq.bzl/0.3.4/source.json": "786dafdc2843722da3416e4343ee1a05237227f068590779a6e8496a2064c0f9",
+ "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/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0",
+ "https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27",
"https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca",
"https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/source.json": "22bc55c47af97246cfc093d0acf683a7869377de362b5d1c552c2c2e16b7a806",
"https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198"
@@ -213,8 +211,8 @@
"moduleExtensions": {
"@@aspect_rules_esbuild+//esbuild:extensions.bzl%esbuild": {
"general": {
- "bzlTransitiveDigest": "c4i5gawrp4Au9UMb55EzQCePYwkrFqD9tFBN7GdHG5g=",
- "usagesDigest": "ToTaCONCN/E05krnHXLM1kpV1zrHNxHrGpUip973II4=",
+ "bzlTransitiveDigest": "QxNkTCcD6yURsbqznKyC/WCjCyhHZbEipGXl2UQjjzY=",
+ "usagesDigest": "6We6zwGoawD9YXqMI0KPaxEKJTnamXBsuOekhFS2D40=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
@@ -269,11 +267,11 @@
"npm__esbuild_0.19.9": {
"repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_rule",
"attributes": {
+ "key": "npm__esbuild_0.19.9",
"package": "esbuild",
"version": "0.19.9",
"root_package": "",
"link_workspace": "",
- "link_packages": {},
"integrity": "sha512-U9CHtKSy+EpPsEBa+/A2gMs/h3ylBC0H0KSqIg7tpztHerLi6nrrcoUJAkNCEPumx8yJ+Byic4BVwHgRbN0TBg==",
"url": "",
"commit": "",
@@ -288,20 +286,23 @@
"npm_auth_password": "",
"lifecycle_hooks": [],
"extra_build_content": "",
+ "generate_package_json_bzl": false,
"generate_bzl_library_targets": false,
"extract_full_archive": false,
- "exclude_package_contents": []
+ "exclude_package_contents": [],
+ "exclude_package_contents_presets": []
}
},
"npm__esbuild_0.19.9__links": {
- "repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_links",
+ "repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_links_rule",
"attributes": {
+ "key": "npm__esbuild_0.19.9",
"package": "esbuild",
"version": "0.19.9",
- "dev": false,
"root_package": "",
- "link_packages": {},
"deps": {},
+ "deps_oss": {},
+ "deps_cpus": {},
"transitive_closure": {},
"lifecycle_build_target": false,
"lifecycle_hooks_env": [],
@@ -314,31 +315,12 @@
"//visibility:public"
],
"replace_package": "",
- "exclude_package_contents": []
+ "exclude_package_contents": [],
+ "exclude_package_contents_presets": []
}
}
},
"recordedRepoMappingEntries": [
- [
- "aspect_bazel_lib+",
- "bazel_lib",
- "bazel_lib+"
- ],
- [
- "aspect_bazel_lib+",
- "bazel_skylib",
- "bazel_skylib+"
- ],
- [
- "aspect_bazel_lib+",
- "bazel_tools",
- "bazel_tools"
- ],
- [
- "aspect_bazel_lib+",
- "tar.bzl",
- "tar.bzl+"
- ],
[
"aspect_rules_esbuild+",
"aspect_rules_js",
@@ -354,11 +336,6 @@
"bazel_skylib",
"bazel_skylib+"
],
- [
- "aspect_rules_js+",
- "aspect_bazel_lib",
- "aspect_bazel_lib+"
- ],
[
"aspect_rules_js+",
"aspect_rules_js",
@@ -371,162 +348,33 @@
],
[
"aspect_rules_js+",
- "bazel_lib",
- "bazel_lib+"
- ],
- [
- "aspect_rules_js+",
- "bazel_skylib",
- "bazel_skylib+"
+ "bazel_features",
+ "bazel_features+"
],
[
"aspect_rules_js+",
- "bazel_tools",
- "bazel_tools"
- ],
- [
- "bazel_lib+",
- "bazel_skylib",
- "bazel_skylib+"
- ],
- [
- "bazel_lib+",
- "bazel_tools",
- "bazel_tools"
- ],
- [
- "tar.bzl+",
- "bazel_lib",
- "bazel_lib+"
- ],
- [
- "tar.bzl+",
- "bazel_skylib",
- "bazel_skylib+"
- ],
- [
- "tar.bzl+",
- "tar.bzl",
- "tar.bzl+"
- ]
- ]
- }
- },
- "@@aspect_rules_js+//npm:extensions.bzl%pnpm": {
- "general": {
- "bzlTransitiveDigest": "HC+l+mTivq1p/KbcVQ+iV5QwYR+oKESJh827FY68SH8=",
- "usagesDigest": "PvqSdyUvIknVzZ66q+9FjDqiPWbKoaSj5J3EB+Z3ZAs=",
- "recordedFileInputs": {},
- "recordedDirentsInputs": {},
- "envVariables": {},
- "generatedRepoSpecs": {
- "pnpm": {
- "repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_rule",
- "attributes": {
- "package": "pnpm",
- "version": "10.29.3",
- "root_package": "",
- "link_workspace": "",
- "link_packages": {},
- "integrity": "sha512-SY4ftMylqgbB3PJhHm+vxQly/+cYmZjECekN50VmREKY/+Q+bNKs3Hdboap8xeCSqLcFTIEbqMV3D4RpPTPS3A==",
- "url": "",
- "commit": "",
- "patch_args": [
- "-p0"
- ],
- "patches": [],
- "custom_postinstall": "",
- "npm_auth": "",
- "npm_auth_basic": "",
- "npm_auth_username": "",
- "npm_auth_password": "",
- "lifecycle_hooks": [],
- "extra_build_content": "load(\"@aspect_rules_js//js:defs.bzl\", \"js_binary\")\njs_binary(name = \"pnpm\", data = glob([\"package/**\"]), entry_point = \"package/dist/pnpm.cjs\", visibility = [\"//visibility:public\"])",
- "generate_bzl_library_targets": false,
- "extract_full_archive": true,
- "exclude_package_contents": []
- }
- },
- "pnpm__links": {
- "repoRuleId": "@@aspect_rules_js+//npm/private:npm_import.bzl%npm_import_links",
- "attributes": {
- "package": "pnpm",
- "version": "10.29.3",
- "dev": false,
- "root_package": "",
- "link_packages": {},
- "deps": {},
- "transitive_closure": {},
- "lifecycle_build_target": false,
- "lifecycle_hooks_env": [],
- "lifecycle_hooks_execution_requirements": [
- "no-sandbox"
- ],
- "lifecycle_hooks_use_default_shell_env": false,
- "bins": {},
- "package_visibility": [
- "//visibility:public"
- ],
- "replace_package": "",
- "exclude_package_contents": []
- }
- }
- },
- "recordedRepoMappingEntries": [
- [
- "aspect_bazel_lib+",
"bazel_lib",
"bazel_lib+"
],
[
- "aspect_bazel_lib+",
+ "aspect_rules_js+",
"bazel_skylib",
"bazel_skylib+"
],
[
- "aspect_bazel_lib+",
+ "aspect_rules_js+",
"bazel_tools",
"bazel_tools"
],
- [
- "aspect_bazel_lib+",
- "tar.bzl",
- "tar.bzl+"
- ],
- [
- "aspect_rules_js+",
- "aspect_bazel_lib",
- "aspect_bazel_lib+"
- ],
- [
- "aspect_rules_js+",
- "aspect_rules_js",
- "aspect_rules_js+"
- ],
- [
- "aspect_rules_js+",
- "aspect_tools_telemetry_report",
- "aspect_tools_telemetry++telemetry+aspect_tools_telemetry_report"
- ],
- [
- "aspect_rules_js+",
- "bazel_features",
- "bazel_features+"
- ],
- [
- "aspect_rules_js+",
- "bazel_lib",
- "bazel_lib+"
- ],
[
"aspect_rules_js+",
- "bazel_skylib",
- "bazel_skylib+"
+ "protobuf",
+ "protobuf+"
],
[
"aspect_rules_js+",
- "bazel_tools",
- "bazel_tools"
+ "tar.bzl",
+ "tar.bzl+"
],
[
"bazel_features+",
@@ -538,6 +386,11 @@
"bazel_features_version",
"bazel_features++version_extension+bazel_features_version"
],
+ [
+ "bazel_lib+",
+ "bazel_lib",
+ "bazel_lib+"
+ ],
[
"bazel_lib+",
"bazel_skylib",
@@ -548,6 +401,11 @@
"bazel_tools",
"bazel_tools"
],
+ [
+ "protobuf+",
+ "proto_bazel_features",
+ "bazel_features+"
+ ],
[
"tar.bzl+",
"bazel_lib",
@@ -568,19 +426,17 @@
},
"@@aspect_rules_ts+//ts:extensions.bzl%ext": {
"general": {
- "bzlTransitiveDigest": "QDTi1Wl/eEY4IgbXjRhegUQfHj+bB8ZEVyiSGLZc6qo=",
- "usagesDigest": "aaqqxEFKCRGFkeAf0pKmXvZZTLGYIk3pQsDFG28ZbNg=",
- "recordedFileInputs": {
- "@@rules_browsers+//package.json": "84dc1ba9b1c667a25894e97218bd8f247d54f24bb694efb397a881be3c06a4c5"
- },
+ "bzlTransitiveDigest": "dhTbv9E6UfT1WJmmu3ORRPO6AKFJvgBjBxu+BO+u1RY=",
+ "usagesDigest": "M9vJ1ahjqDb8ONBLGfZdvp4Vq1o4Y6Xjfv/ZY7PlnYo=",
+ "recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
"generatedRepoSpecs": {
"angular_cli_npm_typescript": {
"repoRuleId": "@@aspect_rules_ts+//ts/private:npm_repositories.bzl%http_archive_version",
"attributes": {
- "version": "5.9.3",
- "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "version": "6.0.2",
+ "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==",
"urls": [
"https://registry.npmjs.org/typescript/-/typescript-{}.tgz"
]
@@ -589,8 +445,8 @@
"rules_angular_npm_typescript": {
"repoRuleId": "@@aspect_rules_ts+//ts/private:npm_repositories.bzl%http_archive_version",
"attributes": {
- "version": "5.9.2",
- "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
+ "version": "5.9.3",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"urls": [
"https://registry.npmjs.org/typescript/-/typescript-{}.tgz"
]
@@ -609,9 +465,8 @@
"npm_rules_browsers_typescript": {
"repoRuleId": "@@aspect_rules_ts+//ts/private:npm_repositories.bzl%http_archive_version",
"attributes": {
- "version": "",
- "version_from": "@@rules_browsers+//:package.json",
- "integrity": "",
+ "version": "5.9.3",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"urls": [
"https://registry.npmjs.org/typescript/-/typescript-{}.tgz"
]
@@ -635,7 +490,7 @@
"@@aspect_tools_telemetry+//:extension.bzl%telemetry": {
"general": {
"bzlTransitiveDigest": "cl5A2O84vDL6Tt+Qga8FCj1DUDGqn+e7ly5rZ+4xvcc=",
- "usagesDigest": "PRLGsERE1Dznyx/OIAl7BPo8mzMvOklnNnZ8zdCpPTE=",
+ "usagesDigest": "0S2z9G3E1NIz6vCXk9IbRcO5LIckEcYVMSzRj2sEML8=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
@@ -644,10 +499,10 @@
"repoRuleId": "@@aspect_tools_telemetry+//:extension.bzl%tel_repository",
"attributes": {
"deps": {
- "aspect_rules_js": "2.9.2",
- "aspect_rules_ts": "3.8.4",
- "aspect_rules_esbuild": "0.25.0",
- "aspect_rules_jasmine": "2.0.2",
+ "aspect_rules_js": "3.0.3",
+ "aspect_rules_ts": "3.8.8",
+ "aspect_rules_esbuild": "0.25.1",
+ "aspect_rules_jasmine": "2.0.4",
"aspect_tools_telemetry": "0.3.3"
}
}
@@ -669,7 +524,7 @@
},
"@@pybind11_bazel+//:python_configure.bzl%extension": {
"general": {
- "bzlTransitiveDigest": "c9ZWWeXeu6bctL4/SsY2otFWyeFN0JJ20+ymGyJZtWk=",
+ "bzlTransitiveDigest": "D2/qWHU6yQFwRG7Bb+caqrYMha5avsASao2vERrxK24=",
"usagesDigest": "fycyB39YnXIJkfWCIXLUKJMZzANcuLy9ZE73hRucjFk=",
"recordedFileInputs": {
"@@pybind11_bazel+//MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e"
@@ -704,7 +559,7 @@
"@@rules_angular+//setup:extensions.bzl%rules_angular": {
"general": {
"bzlTransitiveDigest": "fkaH7HMicL3g7/NDaFzlq39kcLopMyQ3KdbDn+5CRzA=",
- "usagesDigest": "ZinuLP7QHxaW5achD0Vz19qElMu4r2LvGvh96Z5zYlA=",
+ "usagesDigest": "it5nR/3UaQWj2eaaMj+EnoZuL2z/AeTuz7uOnqd+nxo=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
@@ -720,7 +575,7 @@
"repoRuleId": "@@rules_angular+//setup:repositories.bzl%configurable_deps_repo",
"attributes": {
"angular_compiler_cli": "@@rules_angular+//:node_modules/@angular/compiler-cli",
- "typescript": "@@rules_angular+//:node_modules/typescript-local"
+ "typescript": "@@rules_angular+//:node_modules/typescript"
}
},
"dev_infra_rules_angular_configurable_deps": {
@@ -736,8 +591,8 @@
},
"@@rules_browsers+//browsers:extensions.bzl%browsers": {
"general": {
- "bzlTransitiveDigest": "agkaLQ8wE1r/5IX6pkERzFxI/z0M42Em+ICNO6TXsVo=",
- "usagesDigest": "FS7q5WaIwg3KirS3njhuPFkTIBYvDaTInVGrlzu0XL8=",
+ "bzlTransitiveDigest": "Bm6fiKpWy96aLohOlLCP36ARVxRLZm/R+smhsb2HzmI=",
+ "usagesDigest": "FmXYJVoVJlnfUU8x8gObSvu4qWcco/9Faw61aC/wBF0=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
@@ -745,9 +600,9 @@
"rules_browsers_chrome_linux": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
- "sha256": "0a2ff0fc9eb5958b7b420f20e3968f424be7423fef89739e71565a48aa073a57",
+ "sha256": "1ac33f89306327af43be159c03ca4a26486de0858f42fe52394acdef50364143",
"urls": [
- "https://storage.googleapis.com/chrome-for-testing-public/145.0.7586.0/linux64/chrome-headless-shell-linux64.zip"
+ "https://storage.googleapis.com/chrome-for-testing-public/147.0.7687.0/linux64/chrome-headless-shell-linux64.zip"
],
"named_files": {
"CHROME-HEADLESS-SHELL": "chrome-headless-shell-linux64/chrome-headless-shell"
@@ -763,9 +618,9 @@
"rules_browsers_chrome_mac": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
- "sha256": "e6076b1201d86f74c5eab982a239d5af83e66b1aa4d780bcb792698790e01d87",
+ "sha256": "169ff49c465cfda52931395e61861e146dfc5013e92c01ca792db5acea858d0b",
"urls": [
- "https://storage.googleapis.com/chrome-for-testing-public/145.0.7586.0/mac-x64/chrome-headless-shell-mac-x64.zip"
+ "https://storage.googleapis.com/chrome-for-testing-public/147.0.7687.0/mac-x64/chrome-headless-shell-mac-x64.zip"
],
"named_files": {
"CHROME-HEADLESS-SHELL": "chrome-headless-shell-mac-x64/chrome-headless-shell"
@@ -781,9 +636,9 @@
"rules_browsers_chrome_mac_arm": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
- "sha256": "b74dbcf5543d916b02d0a133e2e7c6a4de251f06733f72c2c15ea8c42213f63b",
+ "sha256": "aeaaaaa4d68193a21bed04c44ddeb1230232707b4ea1d845a92925787509cd8e",
"urls": [
- "https://storage.googleapis.com/chrome-for-testing-public/145.0.7586.0/mac-arm64/chrome-headless-shell-mac-arm64.zip"
+ "https://storage.googleapis.com/chrome-for-testing-public/147.0.7687.0/mac-arm64/chrome-headless-shell-mac-arm64.zip"
],
"named_files": {
"CHROME-HEADLESS-SHELL": "chrome-headless-shell-mac-arm64/chrome-headless-shell"
@@ -799,9 +654,9 @@
"rules_browsers_chrome_win64": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
- "sha256": "df1e612dc3b1615e182a1f11821052995913c39df37caa52699de21a68d030d2",
+ "sha256": "4d6d79bcbcb22084df6e3a3d3a2caff67d6c0fa488d63f0c7ec1526f9553db8c",
"urls": [
- "https://storage.googleapis.com/chrome-for-testing-public/145.0.7586.0/win64/chrome-headless-shell-win64.zip"
+ "https://storage.googleapis.com/chrome-for-testing-public/147.0.7687.0/win64/chrome-headless-shell-win64.zip"
],
"named_files": {
"CHROME-HEADLESS-SHELL": "chrome-headless-shell-win64/chrome-headless-shell.exe"
@@ -817,9 +672,9 @@
"rules_browsers_chromedriver_linux": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
- "sha256": "69c504306399d979a2766fea603c3fb9d3d87d46c75bddc9f2a049b4f636d57c",
+ "sha256": "0607ccf6810a07ae08cac6443beac8b23f88dd53c7f1e0299e22d65f7cd2d020",
"urls": [
- "https://storage.googleapis.com/chrome-for-testing-public/145.0.7586.0/linux64/chromedriver-linux64.zip"
+ "https://storage.googleapis.com/chrome-for-testing-public/147.0.7687.0/linux64/chromedriver-linux64.zip"
],
"named_files": {
"CHROMEDRIVER": "chromedriver-linux64/chromedriver"
@@ -833,9 +688,9 @@
"rules_browsers_chromedriver_mac": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
- "sha256": "5fc9d6f594fc5f2568a15145f25116dd8e9c9a60baa8da4bb21a17650fb00e7e",
+ "sha256": "0f512a9dd683ed4c41e609d8d02c07807497dbad3ab2f95f0d583486be7b8cff",
"urls": [
- "https://storage.googleapis.com/chrome-for-testing-public/145.0.7586.0/mac-x64/chromedriver-mac-x64.zip"
+ "https://storage.googleapis.com/chrome-for-testing-public/147.0.7687.0/mac-x64/chromedriver-mac-x64.zip"
],
"named_files": {
"CHROMEDRIVER": "chromedriver-mac-x64/chromedriver"
@@ -849,9 +704,9 @@
"rules_browsers_chromedriver_mac_arm": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
- "sha256": "14e92294c2c3639ca4e7d27e850588b619d698e2f8905cee368f07db2e1bf1e9",
+ "sha256": "7d6fc6d17de1733eb6739d1ea16d085c8df1568bcf9fa0d130c2784b27f38268",
"urls": [
- "https://storage.googleapis.com/chrome-for-testing-public/145.0.7586.0/mac-arm64/chromedriver-mac-arm64.zip"
+ "https://storage.googleapis.com/chrome-for-testing-public/147.0.7687.0/mac-arm64/chromedriver-mac-arm64.zip"
],
"named_files": {
"CHROMEDRIVER": "chromedriver-mac-arm64/chromedriver"
@@ -865,9 +720,9 @@
"rules_browsers_chromedriver_win64": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
- "sha256": "cf641d2e176db95bcc158cd90eafd347ad4928fa0458a5f3bfd56c6d983e70db",
+ "sha256": "f4e9fb7bbf692fde7979b24e8d737b3cef4baafbc7a370e5d0abc4a8450fd830",
"urls": [
- "https://storage.googleapis.com/chrome-for-testing-public/145.0.7586.0/win64/chromedriver-win64.zip"
+ "https://storage.googleapis.com/chrome-for-testing-public/147.0.7687.0/win64/chromedriver-win64.zip"
],
"named_files": {
"CHROMEDRIVER": "chromedriver-win64/chromedriver.exe"
@@ -881,9 +736,9 @@
"rules_browsers_firefox_linux": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
- "sha256": "8d56f479cc398a537a60a3fa20dca92d8a41925113d3a67f534881a4e4d7e344",
+ "sha256": "f055b9c0d7346a10d22edc7f10e08679af2ea495367381ab2be9cab3ec6add97",
"urls": [
- "https://archive.mozilla.org/pub/firefox/releases/146.0/linux-x86_64/en-US/firefox-146.0.tar.xz"
+ "https://archive.mozilla.org/pub/firefox/releases/147.0/linux-x86_64/en-US/firefox-147.0.tar.xz"
],
"named_files": {
"FIREFOX": "firefox/firefox"
@@ -897,9 +752,9 @@
"rules_browsers_firefox_mac": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
- "sha256": "4b1645313887972d466cd82166ea571485c2c40a167f84624e3f3ca739993cc9",
+ "sha256": "48485e2068bc726e2f30cf5855fc2da1fc75c1272bc243a5394f428ffae3ba35",
"urls": [
- "https://archive.mozilla.org/pub/firefox/releases/146.0/mac/en-US/Firefox%20146.0.dmg"
+ "https://archive.mozilla.org/pub/firefox/releases/147.0/mac/en-US/Firefox%20147.0.dmg"
],
"named_files": {
"FIREFOX": "Firefox.app/Contents/MacOS/firefox"
@@ -913,9 +768,9 @@
"rules_browsers_firefox_mac_arm": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
- "sha256": "4b1645313887972d466cd82166ea571485c2c40a167f84624e3f3ca739993cc9",
+ "sha256": "48485e2068bc726e2f30cf5855fc2da1fc75c1272bc243a5394f428ffae3ba35",
"urls": [
- "https://archive.mozilla.org/pub/firefox/releases/146.0/mac/en-US/Firefox%20146.0.dmg"
+ "https://archive.mozilla.org/pub/firefox/releases/147.0/mac/en-US/Firefox%20147.0.dmg"
],
"named_files": {
"FIREFOX": "Firefox.app/Contents/MacOS/firefox"
@@ -929,9 +784,9 @@
"rules_browsers_firefox_win64": {
"repoRuleId": "@@rules_browsers+//browsers/private:browser_repo.bzl%browser_repo",
"attributes": {
- "sha256": "216870c89648f32450cfefb5cec417fcd66d480d5dc83f894bf99f5fd7f38dbb",
+ "sha256": "36ff9e150875aa48a0af9eec3eb67f66dddd8efac5c743265371a72ae3e796c4",
"urls": [
- "https://archive.mozilla.org/pub/firefox/releases/146.0/win64/en-US/Firefox%20Setup%20146.0.exe"
+ "https://archive.mozilla.org/pub/firefox/releases/147.0/win64/en-US/Firefox%20Setup%20147.0.exe"
],
"named_files": {
"FIREFOX": "core/firefox.exe"
@@ -948,7 +803,7 @@
},
"@@rules_fuzzing+//fuzzing/private:extensions.bzl%non_module_dependencies": {
"general": {
- "bzlTransitiveDigest": "WHRlQQnxW7e7XMRBhq7SARkDarLDOAbg6iLaJpk5QYM=",
+ "bzlTransitiveDigest": "4LouzhF/yT117s7peGnNs9ROomiJXC6Zl5R0oI21jho=",
"usagesDigest": "wy6ISK6UOcBEjj/mvJ/S3WeXoO67X+1llb9yPyFtPgc=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
@@ -1031,7 +886,7 @@
},
"@@rules_kotlin+//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": {
"general": {
- "bzlTransitiveDigest": "rL/34P1aFDq2GqVC2zCFgQ8nTuOC6ziogocpvG50Qz8=",
+ "bzlTransitiveDigest": "nvW/NrBXlAmiQw99EMGKkLaD2KbNp2mQDlxdfpr+0Ls=",
"usagesDigest": "QI2z8ZUR+mqtbwsf2fLqYdJAkPOHdOV+tF2yVAUgRzw=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
@@ -1096,7 +951,7 @@
"@@rules_nodejs+//nodejs:extensions.bzl%node": {
"general": {
"bzlTransitiveDigest": "4pUxCNc22K4I+6+4Nxu52Hur12tFRfa1JMsN5mdDv60=",
- "usagesDigest": "6UAmdIABVpqhlkQ3A3NGscf00ds9dFEt+lei3DibyqM=",
+ "usagesDigest": "Y6U29CqmYKc6K0sgS/ygHdxb40MzJPS7jcDCXXExPZw=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
@@ -1106,46 +961,46 @@
"attributes": {
"node_download_auth": {},
"node_repositories": {
- "22.22.0-darwin_arm64": [
- "node-v22.22.0-darwin-arm64.tar.gz",
- "node-v22.22.0-darwin-arm64",
- "5ed4db0fcf1eaf84d91ad12462631d73bf4576c1377e192d222e48026a902640"
+ "22.22.2-darwin_arm64": [
+ "node-v22.22.2-darwin-arm64.tar.gz",
+ "node-v22.22.2-darwin-arm64",
+ "db4b275b83736df67533529a18cc55de2549a8329ace6c7bcc68f8d22d3c9000"
],
- "22.22.0-darwin_amd64": [
- "node-v22.22.0-darwin-x64.tar.gz",
- "node-v22.22.0-darwin-x64",
- "5ea50c9d6dea3dfa3abb66b2656f7a4e1c8cef23432b558d45fb538c7b5dedce"
+ "22.22.2-darwin_amd64": [
+ "node-v22.22.2-darwin-x64.tar.gz",
+ "node-v22.22.2-darwin-x64",
+ "12a6abb9c2902cf48a21120da13f87fde1ed1b71a13330712949e8db818708ba"
],
- "22.22.0-linux_arm64": [
- "node-v22.22.0-linux-arm64.tar.xz",
- "node-v22.22.0-linux-arm64",
- "1bf1eb9ee63ffc4e5d324c0b9b62cf4a289f44332dfef9607cea1a0d9596ba6f"
+ "22.22.2-linux_arm64": [
+ "node-v22.22.2-linux-arm64.tar.xz",
+ "node-v22.22.2-linux-arm64",
+ "e9e1930fd321a470e29bb68f30318bf58e3ecb4acb4f1533fb19c58328a091fe"
],
- "22.22.0-linux_ppc64le": [
- "node-v22.22.0-linux-ppc64le.tar.xz",
- "node-v22.22.0-linux-ppc64le",
- "d83b9957431cc18e1fc143a4b99f89cde7b8a18f53ef392231b4336afd058865"
+ "22.22.2-linux_ppc64le": [
+ "node-v22.22.2-linux-ppc64le.tar.xz",
+ "node-v22.22.2-linux-ppc64le",
+ "14045b5a5030d35ca0030fb7e870bd11a651eb9b57323ebc0021e8d78ac6bac9"
],
- "22.22.0-linux_s390x": [
- "node-v22.22.0-linux-s390x.tar.xz",
- "node-v22.22.0-linux-s390x",
- "5aa0e520689448c4233e8d73f284e8e0634fdcd32b479735698494be5641f3e4"
+ "22.22.2-linux_s390x": [
+ "node-v22.22.2-linux-s390x.tar.xz",
+ "node-v22.22.2-linux-s390x",
+ "9e4a07c291b8949289c6ea8ee61b1d14666a4810feae776a8d1eb1f57e03a2fb"
],
- "22.22.0-linux_amd64": [
- "node-v22.22.0-linux-x64.tar.xz",
- "node-v22.22.0-linux-x64",
- "9aa8e9d2298ab68c600bd6fb86a6c13bce11a4eca1ba9b39d79fa021755d7c37"
+ "22.22.2-linux_amd64": [
+ "node-v22.22.2-linux-x64.tar.xz",
+ "node-v22.22.2-linux-x64",
+ "88fd1ce767091fd8d4a99fdb2356e98c819f93f3b1f8663853a2dee9b438068a"
],
- "22.22.0-windows_amd64": [
- "node-v22.22.0-win-x64.zip",
- "node-v22.22.0-win-x64",
- "c97fa376d2becdc8863fcd3ca2dd9a83a9f3468ee7ccf7a6d076ec66a645c77a"
+ "22.22.2-windows_amd64": [
+ "node-v22.22.2-win-x64.zip",
+ "node-v22.22.2-win-x64",
+ "7c93e9d92bf68c07182b471aa187e35ee6cd08ef0f24ab060dfff605fcc1c57c"
]
},
"node_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
- "node_version": "22.22.0",
+ "node_version": "22.22.2",
"include_headers": false,
"platform": "linux_amd64"
}
@@ -1155,46 +1010,46 @@
"attributes": {
"node_download_auth": {},
"node_repositories": {
- "22.22.0-darwin_arm64": [
- "node-v22.22.0-darwin-arm64.tar.gz",
- "node-v22.22.0-darwin-arm64",
- "5ed4db0fcf1eaf84d91ad12462631d73bf4576c1377e192d222e48026a902640"
+ "22.22.2-darwin_arm64": [
+ "node-v22.22.2-darwin-arm64.tar.gz",
+ "node-v22.22.2-darwin-arm64",
+ "db4b275b83736df67533529a18cc55de2549a8329ace6c7bcc68f8d22d3c9000"
],
- "22.22.0-darwin_amd64": [
- "node-v22.22.0-darwin-x64.tar.gz",
- "node-v22.22.0-darwin-x64",
- "5ea50c9d6dea3dfa3abb66b2656f7a4e1c8cef23432b558d45fb538c7b5dedce"
+ "22.22.2-darwin_amd64": [
+ "node-v22.22.2-darwin-x64.tar.gz",
+ "node-v22.22.2-darwin-x64",
+ "12a6abb9c2902cf48a21120da13f87fde1ed1b71a13330712949e8db818708ba"
],
- "22.22.0-linux_arm64": [
- "node-v22.22.0-linux-arm64.tar.xz",
- "node-v22.22.0-linux-arm64",
- "1bf1eb9ee63ffc4e5d324c0b9b62cf4a289f44332dfef9607cea1a0d9596ba6f"
+ "22.22.2-linux_arm64": [
+ "node-v22.22.2-linux-arm64.tar.xz",
+ "node-v22.22.2-linux-arm64",
+ "e9e1930fd321a470e29bb68f30318bf58e3ecb4acb4f1533fb19c58328a091fe"
],
- "22.22.0-linux_ppc64le": [
- "node-v22.22.0-linux-ppc64le.tar.xz",
- "node-v22.22.0-linux-ppc64le",
- "d83b9957431cc18e1fc143a4b99f89cde7b8a18f53ef392231b4336afd058865"
+ "22.22.2-linux_ppc64le": [
+ "node-v22.22.2-linux-ppc64le.tar.xz",
+ "node-v22.22.2-linux-ppc64le",
+ "14045b5a5030d35ca0030fb7e870bd11a651eb9b57323ebc0021e8d78ac6bac9"
],
- "22.22.0-linux_s390x": [
- "node-v22.22.0-linux-s390x.tar.xz",
- "node-v22.22.0-linux-s390x",
- "5aa0e520689448c4233e8d73f284e8e0634fdcd32b479735698494be5641f3e4"
+ "22.22.2-linux_s390x": [
+ "node-v22.22.2-linux-s390x.tar.xz",
+ "node-v22.22.2-linux-s390x",
+ "9e4a07c291b8949289c6ea8ee61b1d14666a4810feae776a8d1eb1f57e03a2fb"
],
- "22.22.0-linux_amd64": [
- "node-v22.22.0-linux-x64.tar.xz",
- "node-v22.22.0-linux-x64",
- "9aa8e9d2298ab68c600bd6fb86a6c13bce11a4eca1ba9b39d79fa021755d7c37"
+ "22.22.2-linux_amd64": [
+ "node-v22.22.2-linux-x64.tar.xz",
+ "node-v22.22.2-linux-x64",
+ "88fd1ce767091fd8d4a99fdb2356e98c819f93f3b1f8663853a2dee9b438068a"
],
- "22.22.0-windows_amd64": [
- "node-v22.22.0-win-x64.zip",
- "node-v22.22.0-win-x64",
- "c97fa376d2becdc8863fcd3ca2dd9a83a9f3468ee7ccf7a6d076ec66a645c77a"
+ "22.22.2-windows_amd64": [
+ "node-v22.22.2-win-x64.zip",
+ "node-v22.22.2-win-x64",
+ "7c93e9d92bf68c07182b471aa187e35ee6cd08ef0f24ab060dfff605fcc1c57c"
]
},
"node_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
- "node_version": "22.22.0",
+ "node_version": "22.22.2",
"include_headers": false,
"platform": "linux_arm64"
}
@@ -1204,46 +1059,46 @@
"attributes": {
"node_download_auth": {},
"node_repositories": {
- "22.22.0-darwin_arm64": [
- "node-v22.22.0-darwin-arm64.tar.gz",
- "node-v22.22.0-darwin-arm64",
- "5ed4db0fcf1eaf84d91ad12462631d73bf4576c1377e192d222e48026a902640"
+ "22.22.2-darwin_arm64": [
+ "node-v22.22.2-darwin-arm64.tar.gz",
+ "node-v22.22.2-darwin-arm64",
+ "db4b275b83736df67533529a18cc55de2549a8329ace6c7bcc68f8d22d3c9000"
],
- "22.22.0-darwin_amd64": [
- "node-v22.22.0-darwin-x64.tar.gz",
- "node-v22.22.0-darwin-x64",
- "5ea50c9d6dea3dfa3abb66b2656f7a4e1c8cef23432b558d45fb538c7b5dedce"
+ "22.22.2-darwin_amd64": [
+ "node-v22.22.2-darwin-x64.tar.gz",
+ "node-v22.22.2-darwin-x64",
+ "12a6abb9c2902cf48a21120da13f87fde1ed1b71a13330712949e8db818708ba"
],
- "22.22.0-linux_arm64": [
- "node-v22.22.0-linux-arm64.tar.xz",
- "node-v22.22.0-linux-arm64",
- "1bf1eb9ee63ffc4e5d324c0b9b62cf4a289f44332dfef9607cea1a0d9596ba6f"
+ "22.22.2-linux_arm64": [
+ "node-v22.22.2-linux-arm64.tar.xz",
+ "node-v22.22.2-linux-arm64",
+ "e9e1930fd321a470e29bb68f30318bf58e3ecb4acb4f1533fb19c58328a091fe"
],
- "22.22.0-linux_ppc64le": [
- "node-v22.22.0-linux-ppc64le.tar.xz",
- "node-v22.22.0-linux-ppc64le",
- "d83b9957431cc18e1fc143a4b99f89cde7b8a18f53ef392231b4336afd058865"
+ "22.22.2-linux_ppc64le": [
+ "node-v22.22.2-linux-ppc64le.tar.xz",
+ "node-v22.22.2-linux-ppc64le",
+ "14045b5a5030d35ca0030fb7e870bd11a651eb9b57323ebc0021e8d78ac6bac9"
],
- "22.22.0-linux_s390x": [
- "node-v22.22.0-linux-s390x.tar.xz",
- "node-v22.22.0-linux-s390x",
- "5aa0e520689448c4233e8d73f284e8e0634fdcd32b479735698494be5641f3e4"
+ "22.22.2-linux_s390x": [
+ "node-v22.22.2-linux-s390x.tar.xz",
+ "node-v22.22.2-linux-s390x",
+ "9e4a07c291b8949289c6ea8ee61b1d14666a4810feae776a8d1eb1f57e03a2fb"
],
- "22.22.0-linux_amd64": [
- "node-v22.22.0-linux-x64.tar.xz",
- "node-v22.22.0-linux-x64",
- "9aa8e9d2298ab68c600bd6fb86a6c13bce11a4eca1ba9b39d79fa021755d7c37"
+ "22.22.2-linux_amd64": [
+ "node-v22.22.2-linux-x64.tar.xz",
+ "node-v22.22.2-linux-x64",
+ "88fd1ce767091fd8d4a99fdb2356e98c819f93f3b1f8663853a2dee9b438068a"
],
- "22.22.0-windows_amd64": [
- "node-v22.22.0-win-x64.zip",
- "node-v22.22.0-win-x64",
- "c97fa376d2becdc8863fcd3ca2dd9a83a9f3468ee7ccf7a6d076ec66a645c77a"
+ "22.22.2-windows_amd64": [
+ "node-v22.22.2-win-x64.zip",
+ "node-v22.22.2-win-x64",
+ "7c93e9d92bf68c07182b471aa187e35ee6cd08ef0f24ab060dfff605fcc1c57c"
]
},
"node_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
- "node_version": "22.22.0",
+ "node_version": "22.22.2",
"include_headers": false,
"platform": "linux_s390x"
}
@@ -1253,20 +1108,283 @@
"attributes": {
"node_download_auth": {},
"node_repositories": {
- "22.22.0-darwin_arm64": [
- "node-v22.22.0-darwin-arm64.tar.gz",
- "node-v22.22.0-darwin-arm64",
- "5ed4db0fcf1eaf84d91ad12462631d73bf4576c1377e192d222e48026a902640"
+ "22.22.2-darwin_arm64": [
+ "node-v22.22.2-darwin-arm64.tar.gz",
+ "node-v22.22.2-darwin-arm64",
+ "db4b275b83736df67533529a18cc55de2549a8329ace6c7bcc68f8d22d3c9000"
],
- "22.22.0-darwin_amd64": [
- "node-v22.22.0-darwin-x64.tar.gz",
- "node-v22.22.0-darwin-x64",
- "5ea50c9d6dea3dfa3abb66b2656f7a4e1c8cef23432b558d45fb538c7b5dedce"
+ "22.22.2-darwin_amd64": [
+ "node-v22.22.2-darwin-x64.tar.gz",
+ "node-v22.22.2-darwin-x64",
+ "12a6abb9c2902cf48a21120da13f87fde1ed1b71a13330712949e8db818708ba"
],
- "22.22.0-linux_arm64": [
- "node-v22.22.0-linux-arm64.tar.xz",
- "node-v22.22.0-linux-arm64",
- "1bf1eb9ee63ffc4e5d324c0b9b62cf4a289f44332dfef9607cea1a0d9596ba6f"
+ "22.22.2-linux_arm64": [
+ "node-v22.22.2-linux-arm64.tar.xz",
+ "node-v22.22.2-linux-arm64",
+ "e9e1930fd321a470e29bb68f30318bf58e3ecb4acb4f1533fb19c58328a091fe"
+ ],
+ "22.22.2-linux_ppc64le": [
+ "node-v22.22.2-linux-ppc64le.tar.xz",
+ "node-v22.22.2-linux-ppc64le",
+ "14045b5a5030d35ca0030fb7e870bd11a651eb9b57323ebc0021e8d78ac6bac9"
+ ],
+ "22.22.2-linux_s390x": [
+ "node-v22.22.2-linux-s390x.tar.xz",
+ "node-v22.22.2-linux-s390x",
+ "9e4a07c291b8949289c6ea8ee61b1d14666a4810feae776a8d1eb1f57e03a2fb"
+ ],
+ "22.22.2-linux_amd64": [
+ "node-v22.22.2-linux-x64.tar.xz",
+ "node-v22.22.2-linux-x64",
+ "88fd1ce767091fd8d4a99fdb2356e98c819f93f3b1f8663853a2dee9b438068a"
+ ],
+ "22.22.2-windows_amd64": [
+ "node-v22.22.2-win-x64.zip",
+ "node-v22.22.2-win-x64",
+ "7c93e9d92bf68c07182b471aa187e35ee6cd08ef0f24ab060dfff605fcc1c57c"
+ ]
+ },
+ "node_urls": [
+ "https://nodejs.org/dist/v{version}/{filename}"
+ ],
+ "node_version": "22.22.2",
+ "include_headers": false,
+ "platform": "linux_ppc64le"
+ }
+ },
+ "nodejs_darwin_amd64": {
+ "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
+ "attributes": {
+ "node_download_auth": {},
+ "node_repositories": {
+ "22.22.2-darwin_arm64": [
+ "node-v22.22.2-darwin-arm64.tar.gz",
+ "node-v22.22.2-darwin-arm64",
+ "db4b275b83736df67533529a18cc55de2549a8329ace6c7bcc68f8d22d3c9000"
+ ],
+ "22.22.2-darwin_amd64": [
+ "node-v22.22.2-darwin-x64.tar.gz",
+ "node-v22.22.2-darwin-x64",
+ "12a6abb9c2902cf48a21120da13f87fde1ed1b71a13330712949e8db818708ba"
+ ],
+ "22.22.2-linux_arm64": [
+ "node-v22.22.2-linux-arm64.tar.xz",
+ "node-v22.22.2-linux-arm64",
+ "e9e1930fd321a470e29bb68f30318bf58e3ecb4acb4f1533fb19c58328a091fe"
+ ],
+ "22.22.2-linux_ppc64le": [
+ "node-v22.22.2-linux-ppc64le.tar.xz",
+ "node-v22.22.2-linux-ppc64le",
+ "14045b5a5030d35ca0030fb7e870bd11a651eb9b57323ebc0021e8d78ac6bac9"
+ ],
+ "22.22.2-linux_s390x": [
+ "node-v22.22.2-linux-s390x.tar.xz",
+ "node-v22.22.2-linux-s390x",
+ "9e4a07c291b8949289c6ea8ee61b1d14666a4810feae776a8d1eb1f57e03a2fb"
+ ],
+ "22.22.2-linux_amd64": [
+ "node-v22.22.2-linux-x64.tar.xz",
+ "node-v22.22.2-linux-x64",
+ "88fd1ce767091fd8d4a99fdb2356e98c819f93f3b1f8663853a2dee9b438068a"
+ ],
+ "22.22.2-windows_amd64": [
+ "node-v22.22.2-win-x64.zip",
+ "node-v22.22.2-win-x64",
+ "7c93e9d92bf68c07182b471aa187e35ee6cd08ef0f24ab060dfff605fcc1c57c"
+ ]
+ },
+ "node_urls": [
+ "https://nodejs.org/dist/v{version}/{filename}"
+ ],
+ "node_version": "22.22.2",
+ "include_headers": false,
+ "platform": "darwin_amd64"
+ }
+ },
+ "nodejs_darwin_arm64": {
+ "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
+ "attributes": {
+ "node_download_auth": {},
+ "node_repositories": {
+ "22.22.2-darwin_arm64": [
+ "node-v22.22.2-darwin-arm64.tar.gz",
+ "node-v22.22.2-darwin-arm64",
+ "db4b275b83736df67533529a18cc55de2549a8329ace6c7bcc68f8d22d3c9000"
+ ],
+ "22.22.2-darwin_amd64": [
+ "node-v22.22.2-darwin-x64.tar.gz",
+ "node-v22.22.2-darwin-x64",
+ "12a6abb9c2902cf48a21120da13f87fde1ed1b71a13330712949e8db818708ba"
+ ],
+ "22.22.2-linux_arm64": [
+ "node-v22.22.2-linux-arm64.tar.xz",
+ "node-v22.22.2-linux-arm64",
+ "e9e1930fd321a470e29bb68f30318bf58e3ecb4acb4f1533fb19c58328a091fe"
+ ],
+ "22.22.2-linux_ppc64le": [
+ "node-v22.22.2-linux-ppc64le.tar.xz",
+ "node-v22.22.2-linux-ppc64le",
+ "14045b5a5030d35ca0030fb7e870bd11a651eb9b57323ebc0021e8d78ac6bac9"
+ ],
+ "22.22.2-linux_s390x": [
+ "node-v22.22.2-linux-s390x.tar.xz",
+ "node-v22.22.2-linux-s390x",
+ "9e4a07c291b8949289c6ea8ee61b1d14666a4810feae776a8d1eb1f57e03a2fb"
+ ],
+ "22.22.2-linux_amd64": [
+ "node-v22.22.2-linux-x64.tar.xz",
+ "node-v22.22.2-linux-x64",
+ "88fd1ce767091fd8d4a99fdb2356e98c819f93f3b1f8663853a2dee9b438068a"
+ ],
+ "22.22.2-windows_amd64": [
+ "node-v22.22.2-win-x64.zip",
+ "node-v22.22.2-win-x64",
+ "7c93e9d92bf68c07182b471aa187e35ee6cd08ef0f24ab060dfff605fcc1c57c"
+ ]
+ },
+ "node_urls": [
+ "https://nodejs.org/dist/v{version}/{filename}"
+ ],
+ "node_version": "22.22.2",
+ "include_headers": false,
+ "platform": "darwin_arm64"
+ }
+ },
+ "nodejs_windows_amd64": {
+ "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
+ "attributes": {
+ "node_download_auth": {},
+ "node_repositories": {
+ "22.22.2-darwin_arm64": [
+ "node-v22.22.2-darwin-arm64.tar.gz",
+ "node-v22.22.2-darwin-arm64",
+ "db4b275b83736df67533529a18cc55de2549a8329ace6c7bcc68f8d22d3c9000"
+ ],
+ "22.22.2-darwin_amd64": [
+ "node-v22.22.2-darwin-x64.tar.gz",
+ "node-v22.22.2-darwin-x64",
+ "12a6abb9c2902cf48a21120da13f87fde1ed1b71a13330712949e8db818708ba"
+ ],
+ "22.22.2-linux_arm64": [
+ "node-v22.22.2-linux-arm64.tar.xz",
+ "node-v22.22.2-linux-arm64",
+ "e9e1930fd321a470e29bb68f30318bf58e3ecb4acb4f1533fb19c58328a091fe"
+ ],
+ "22.22.2-linux_ppc64le": [
+ "node-v22.22.2-linux-ppc64le.tar.xz",
+ "node-v22.22.2-linux-ppc64le",
+ "14045b5a5030d35ca0030fb7e870bd11a651eb9b57323ebc0021e8d78ac6bac9"
+ ],
+ "22.22.2-linux_s390x": [
+ "node-v22.22.2-linux-s390x.tar.xz",
+ "node-v22.22.2-linux-s390x",
+ "9e4a07c291b8949289c6ea8ee61b1d14666a4810feae776a8d1eb1f57e03a2fb"
+ ],
+ "22.22.2-linux_amd64": [
+ "node-v22.22.2-linux-x64.tar.xz",
+ "node-v22.22.2-linux-x64",
+ "88fd1ce767091fd8d4a99fdb2356e98c819f93f3b1f8663853a2dee9b438068a"
+ ],
+ "22.22.2-windows_amd64": [
+ "node-v22.22.2-win-x64.zip",
+ "node-v22.22.2-win-x64",
+ "7c93e9d92bf68c07182b471aa187e35ee6cd08ef0f24ab060dfff605fcc1c57c"
+ ]
+ },
+ "node_urls": [
+ "https://nodejs.org/dist/v{version}/{filename}"
+ ],
+ "node_version": "22.22.2",
+ "include_headers": false,
+ "platform": "windows_amd64"
+ }
+ },
+ "nodejs_windows_arm64": {
+ "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
+ "attributes": {
+ "node_download_auth": {},
+ "node_repositories": {
+ "22.22.2-darwin_arm64": [
+ "node-v22.22.2-darwin-arm64.tar.gz",
+ "node-v22.22.2-darwin-arm64",
+ "db4b275b83736df67533529a18cc55de2549a8329ace6c7bcc68f8d22d3c9000"
+ ],
+ "22.22.2-darwin_amd64": [
+ "node-v22.22.2-darwin-x64.tar.gz",
+ "node-v22.22.2-darwin-x64",
+ "12a6abb9c2902cf48a21120da13f87fde1ed1b71a13330712949e8db818708ba"
+ ],
+ "22.22.2-linux_arm64": [
+ "node-v22.22.2-linux-arm64.tar.xz",
+ "node-v22.22.2-linux-arm64",
+ "e9e1930fd321a470e29bb68f30318bf58e3ecb4acb4f1533fb19c58328a091fe"
+ ],
+ "22.22.2-linux_ppc64le": [
+ "node-v22.22.2-linux-ppc64le.tar.xz",
+ "node-v22.22.2-linux-ppc64le",
+ "14045b5a5030d35ca0030fb7e870bd11a651eb9b57323ebc0021e8d78ac6bac9"
+ ],
+ "22.22.2-linux_s390x": [
+ "node-v22.22.2-linux-s390x.tar.xz",
+ "node-v22.22.2-linux-s390x",
+ "9e4a07c291b8949289c6ea8ee61b1d14666a4810feae776a8d1eb1f57e03a2fb"
+ ],
+ "22.22.2-linux_amd64": [
+ "node-v22.22.2-linux-x64.tar.xz",
+ "node-v22.22.2-linux-x64",
+ "88fd1ce767091fd8d4a99fdb2356e98c819f93f3b1f8663853a2dee9b438068a"
+ ],
+ "22.22.2-windows_amd64": [
+ "node-v22.22.2-win-x64.zip",
+ "node-v22.22.2-win-x64",
+ "7c93e9d92bf68c07182b471aa187e35ee6cd08ef0f24ab060dfff605fcc1c57c"
+ ]
+ },
+ "node_urls": [
+ "https://nodejs.org/dist/v{version}/{filename}"
+ ],
+ "node_version": "22.22.2",
+ "include_headers": false,
+ "platform": "windows_arm64"
+ }
+ },
+ "nodejs": {
+ "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias",
+ "attributes": {
+ "user_node_repository_name": "nodejs"
+ }
+ },
+ "nodejs_host": {
+ "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias",
+ "attributes": {
+ "user_node_repository_name": "nodejs"
+ }
+ },
+ "nodejs_toolchains": {
+ "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_toolchains_repo.bzl%nodejs_toolchains_repo",
+ "attributes": {
+ "user_node_repository_name": "nodejs"
+ }
+ },
+ "node22_linux_amd64": {
+ "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
+ "attributes": {
+ "node_download_auth": {},
+ "node_repositories": {
+ "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",
@@ -1294,10 +1412,10 @@
],
"node_version": "22.22.0",
"include_headers": false,
- "platform": "linux_ppc64le"
+ "platform": "linux_amd64"
}
},
- "nodejs_darwin_amd64": {
+ "node22_linux_arm64": {
"repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
"attributes": {
"node_download_auth": {},
@@ -1343,10 +1461,10 @@
],
"node_version": "22.22.0",
"include_headers": false,
- "platform": "darwin_amd64"
+ "platform": "linux_arm64"
}
},
- "nodejs_darwin_arm64": {
+ "node22_linux_s390x": {
"repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
"attributes": {
"node_download_auth": {},
@@ -1392,10 +1510,10 @@
],
"node_version": "22.22.0",
"include_headers": false,
- "platform": "darwin_arm64"
+ "platform": "linux_s390x"
}
},
- "nodejs_windows_amd64": {
+ "node22_linux_ppc64le": {
"repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
"attributes": {
"node_download_auth": {},
@@ -1441,10 +1559,10 @@
],
"node_version": "22.22.0",
"include_headers": false,
- "platform": "windows_amd64"
+ "platform": "linux_ppc64le"
}
},
- "nodejs_windows_arm64": {
+ "node22_darwin_amd64": {
"repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
"attributes": {
"node_download_auth": {},
@@ -1490,211 +1608,6 @@
],
"node_version": "22.22.0",
"include_headers": false,
- "platform": "windows_arm64"
- }
- },
- "nodejs": {
- "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias",
- "attributes": {
- "user_node_repository_name": "nodejs"
- }
- },
- "nodejs_host": {
- "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias",
- "attributes": {
- "user_node_repository_name": "nodejs"
- }
- },
- "nodejs_toolchains": {
- "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_toolchains_repo.bzl%nodejs_toolchains_repo",
- "attributes": {
- "user_node_repository_name": "nodejs"
- }
- },
- "node20_linux_amd64": {
- "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
- "attributes": {
- "node_download_auth": {},
- "node_repositories": {},
- "node_urls": [
- "https://nodejs.org/dist/v{version}/{filename}"
- ],
- "node_version": "20.19.0",
- "include_headers": false,
- "platform": "linux_amd64"
- }
- },
- "node20_linux_arm64": {
- "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
- "attributes": {
- "node_download_auth": {},
- "node_repositories": {},
- "node_urls": [
- "https://nodejs.org/dist/v{version}/{filename}"
- ],
- "node_version": "20.19.0",
- "include_headers": false,
- "platform": "linux_arm64"
- }
- },
- "node20_linux_s390x": {
- "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
- "attributes": {
- "node_download_auth": {},
- "node_repositories": {},
- "node_urls": [
- "https://nodejs.org/dist/v{version}/{filename}"
- ],
- "node_version": "20.19.0",
- "include_headers": false,
- "platform": "linux_s390x"
- }
- },
- "node20_linux_ppc64le": {
- "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
- "attributes": {
- "node_download_auth": {},
- "node_repositories": {},
- "node_urls": [
- "https://nodejs.org/dist/v{version}/{filename}"
- ],
- "node_version": "20.19.0",
- "include_headers": false,
- "platform": "linux_ppc64le"
- }
- },
- "node20_darwin_amd64": {
- "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
- "attributes": {
- "node_download_auth": {},
- "node_repositories": {},
- "node_urls": [
- "https://nodejs.org/dist/v{version}/{filename}"
- ],
- "node_version": "20.19.0",
- "include_headers": false,
- "platform": "darwin_amd64"
- }
- },
- "node20_darwin_arm64": {
- "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
- "attributes": {
- "node_download_auth": {},
- "node_repositories": {},
- "node_urls": [
- "https://nodejs.org/dist/v{version}/{filename}"
- ],
- "node_version": "20.19.0",
- "include_headers": false,
- "platform": "darwin_arm64"
- }
- },
- "node20_windows_amd64": {
- "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
- "attributes": {
- "node_download_auth": {},
- "node_repositories": {},
- "node_urls": [
- "https://nodejs.org/dist/v{version}/{filename}"
- ],
- "node_version": "20.19.0",
- "include_headers": false,
- "platform": "windows_amd64"
- }
- },
- "node20_windows_arm64": {
- "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
- "attributes": {
- "node_download_auth": {},
- "node_repositories": {},
- "node_urls": [
- "https://nodejs.org/dist/v{version}/{filename}"
- ],
- "node_version": "20.19.0",
- "include_headers": false,
- "platform": "windows_arm64"
- }
- },
- "node20": {
- "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias",
- "attributes": {
- "user_node_repository_name": "node20"
- }
- },
- "node20_host": {
- "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_repo_host_os_alias.bzl%nodejs_repo_host_os_alias",
- "attributes": {
- "user_node_repository_name": "node20"
- }
- },
- "node20_toolchains": {
- "repoRuleId": "@@rules_nodejs+//nodejs/private:nodejs_toolchains_repo.bzl%nodejs_toolchains_repo",
- "attributes": {
- "user_node_repository_name": "node20"
- }
- },
- "node22_linux_amd64": {
- "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
- "attributes": {
- "node_download_auth": {},
- "node_repositories": {},
- "node_urls": [
- "https://nodejs.org/dist/v{version}/{filename}"
- ],
- "node_version": "22.12.0",
- "include_headers": false,
- "platform": "linux_amd64"
- }
- },
- "node22_linux_arm64": {
- "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
- "attributes": {
- "node_download_auth": {},
- "node_repositories": {},
- "node_urls": [
- "https://nodejs.org/dist/v{version}/{filename}"
- ],
- "node_version": "22.12.0",
- "include_headers": false,
- "platform": "linux_arm64"
- }
- },
- "node22_linux_s390x": {
- "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
- "attributes": {
- "node_download_auth": {},
- "node_repositories": {},
- "node_urls": [
- "https://nodejs.org/dist/v{version}/{filename}"
- ],
- "node_version": "22.12.0",
- "include_headers": false,
- "platform": "linux_s390x"
- }
- },
- "node22_linux_ppc64le": {
- "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
- "attributes": {
- "node_download_auth": {},
- "node_repositories": {},
- "node_urls": [
- "https://nodejs.org/dist/v{version}/{filename}"
- ],
- "node_version": "22.12.0",
- "include_headers": false,
- "platform": "linux_ppc64le"
- }
- },
- "node22_darwin_amd64": {
- "repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
- "attributes": {
- "node_download_auth": {},
- "node_repositories": {},
- "node_urls": [
- "https://nodejs.org/dist/v{version}/{filename}"
- ],
- "node_version": "22.12.0",
- "include_headers": false,
"platform": "darwin_amd64"
}
},
@@ -1702,11 +1615,47 @@
"repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
"attributes": {
"node_download_auth": {},
- "node_repositories": {},
+ "node_repositories": {
+ "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_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
- "node_version": "22.12.0",
+ "node_version": "22.22.0",
"include_headers": false,
"platform": "darwin_arm64"
}
@@ -1715,11 +1664,47 @@
"repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
"attributes": {
"node_download_auth": {},
- "node_repositories": {},
+ "node_repositories": {
+ "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_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
- "node_version": "22.12.0",
+ "node_version": "22.22.0",
"include_headers": false,
"platform": "windows_amd64"
}
@@ -1728,11 +1713,47 @@
"repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
"attributes": {
"node_download_auth": {},
- "node_repositories": {},
+ "node_repositories": {
+ "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_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
- "node_version": "22.12.0",
+ "node_version": "22.22.0",
"include_headers": false,
"platform": "windows_arm64"
}
@@ -1759,11 +1780,47 @@
"repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
"attributes": {
"node_download_auth": {},
- "node_repositories": {},
+ "node_repositories": {
+ "24.13.1-darwin_arm64": [
+ "node-v24.13.1-darwin-arm64.tar.gz",
+ "node-v24.13.1-darwin-arm64",
+ "8c039d59f2fec6195e4281ad5b0d02b9a940897b4df7b849c6fb48be6787bba6"
+ ],
+ "24.13.1-darwin_amd64": [
+ "node-v24.13.1-darwin-x64.tar.gz",
+ "node-v24.13.1-darwin-x64",
+ "527f0578d9812e7dfa225121bda0b1546a6a0e4b5f556295fc8299c272de5fbf"
+ ],
+ "24.13.1-linux_arm64": [
+ "node-v24.13.1-linux-arm64.tar.xz",
+ "node-v24.13.1-linux-arm64",
+ "c827d3d301e2eed1a51f36d0116b71b9e3d9e3b728f081615270ea40faac34c1"
+ ],
+ "24.13.1-linux_ppc64le": [
+ "node-v24.13.1-linux-ppc64le.tar.xz",
+ "node-v24.13.1-linux-ppc64le",
+ "fb712a08d317655dbf776c90f60ac2105109d802e33811df6c9ed33d12f801c6"
+ ],
+ "24.13.1-linux_s390x": [
+ "node-v24.13.1-linux-s390x.tar.xz",
+ "node-v24.13.1-linux-s390x",
+ "8e2c0d9b5545c3db22623e8cb8d6f0c28fcd470f29d32dbeabf9432dda289de2"
+ ],
+ "24.13.1-linux_amd64": [
+ "node-v24.13.1-linux-x64.tar.xz",
+ "node-v24.13.1-linux-x64",
+ "30215f90ea3cd04dfbc06e762c021393fa173a1d392974298bbc871a8e461089"
+ ],
+ "24.13.1-windows_amd64": [
+ "node-v24.13.1-win-x64.zip",
+ "node-v24.13.1-win-x64",
+ "fba577c4bb87df04d54dd87bbdaa5a2272f1f99a2acbf9152e1a91b8b5f0b279"
+ ]
+ },
"node_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
- "node_version": "24.0.0",
+ "node_version": "24.13.1",
"include_headers": false,
"platform": "linux_amd64"
}
@@ -1772,11 +1829,47 @@
"repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
"attributes": {
"node_download_auth": {},
- "node_repositories": {},
+ "node_repositories": {
+ "24.13.1-darwin_arm64": [
+ "node-v24.13.1-darwin-arm64.tar.gz",
+ "node-v24.13.1-darwin-arm64",
+ "8c039d59f2fec6195e4281ad5b0d02b9a940897b4df7b849c6fb48be6787bba6"
+ ],
+ "24.13.1-darwin_amd64": [
+ "node-v24.13.1-darwin-x64.tar.gz",
+ "node-v24.13.1-darwin-x64",
+ "527f0578d9812e7dfa225121bda0b1546a6a0e4b5f556295fc8299c272de5fbf"
+ ],
+ "24.13.1-linux_arm64": [
+ "node-v24.13.1-linux-arm64.tar.xz",
+ "node-v24.13.1-linux-arm64",
+ "c827d3d301e2eed1a51f36d0116b71b9e3d9e3b728f081615270ea40faac34c1"
+ ],
+ "24.13.1-linux_ppc64le": [
+ "node-v24.13.1-linux-ppc64le.tar.xz",
+ "node-v24.13.1-linux-ppc64le",
+ "fb712a08d317655dbf776c90f60ac2105109d802e33811df6c9ed33d12f801c6"
+ ],
+ "24.13.1-linux_s390x": [
+ "node-v24.13.1-linux-s390x.tar.xz",
+ "node-v24.13.1-linux-s390x",
+ "8e2c0d9b5545c3db22623e8cb8d6f0c28fcd470f29d32dbeabf9432dda289de2"
+ ],
+ "24.13.1-linux_amd64": [
+ "node-v24.13.1-linux-x64.tar.xz",
+ "node-v24.13.1-linux-x64",
+ "30215f90ea3cd04dfbc06e762c021393fa173a1d392974298bbc871a8e461089"
+ ],
+ "24.13.1-windows_amd64": [
+ "node-v24.13.1-win-x64.zip",
+ "node-v24.13.1-win-x64",
+ "fba577c4bb87df04d54dd87bbdaa5a2272f1f99a2acbf9152e1a91b8b5f0b279"
+ ]
+ },
"node_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
- "node_version": "24.0.0",
+ "node_version": "24.13.1",
"include_headers": false,
"platform": "linux_arm64"
}
@@ -1785,11 +1878,47 @@
"repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
"attributes": {
"node_download_auth": {},
- "node_repositories": {},
+ "node_repositories": {
+ "24.13.1-darwin_arm64": [
+ "node-v24.13.1-darwin-arm64.tar.gz",
+ "node-v24.13.1-darwin-arm64",
+ "8c039d59f2fec6195e4281ad5b0d02b9a940897b4df7b849c6fb48be6787bba6"
+ ],
+ "24.13.1-darwin_amd64": [
+ "node-v24.13.1-darwin-x64.tar.gz",
+ "node-v24.13.1-darwin-x64",
+ "527f0578d9812e7dfa225121bda0b1546a6a0e4b5f556295fc8299c272de5fbf"
+ ],
+ "24.13.1-linux_arm64": [
+ "node-v24.13.1-linux-arm64.tar.xz",
+ "node-v24.13.1-linux-arm64",
+ "c827d3d301e2eed1a51f36d0116b71b9e3d9e3b728f081615270ea40faac34c1"
+ ],
+ "24.13.1-linux_ppc64le": [
+ "node-v24.13.1-linux-ppc64le.tar.xz",
+ "node-v24.13.1-linux-ppc64le",
+ "fb712a08d317655dbf776c90f60ac2105109d802e33811df6c9ed33d12f801c6"
+ ],
+ "24.13.1-linux_s390x": [
+ "node-v24.13.1-linux-s390x.tar.xz",
+ "node-v24.13.1-linux-s390x",
+ "8e2c0d9b5545c3db22623e8cb8d6f0c28fcd470f29d32dbeabf9432dda289de2"
+ ],
+ "24.13.1-linux_amd64": [
+ "node-v24.13.1-linux-x64.tar.xz",
+ "node-v24.13.1-linux-x64",
+ "30215f90ea3cd04dfbc06e762c021393fa173a1d392974298bbc871a8e461089"
+ ],
+ "24.13.1-windows_amd64": [
+ "node-v24.13.1-win-x64.zip",
+ "node-v24.13.1-win-x64",
+ "fba577c4bb87df04d54dd87bbdaa5a2272f1f99a2acbf9152e1a91b8b5f0b279"
+ ]
+ },
"node_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
- "node_version": "24.0.0",
+ "node_version": "24.13.1",
"include_headers": false,
"platform": "linux_s390x"
}
@@ -1798,11 +1927,47 @@
"repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
"attributes": {
"node_download_auth": {},
- "node_repositories": {},
+ "node_repositories": {
+ "24.13.1-darwin_arm64": [
+ "node-v24.13.1-darwin-arm64.tar.gz",
+ "node-v24.13.1-darwin-arm64",
+ "8c039d59f2fec6195e4281ad5b0d02b9a940897b4df7b849c6fb48be6787bba6"
+ ],
+ "24.13.1-darwin_amd64": [
+ "node-v24.13.1-darwin-x64.tar.gz",
+ "node-v24.13.1-darwin-x64",
+ "527f0578d9812e7dfa225121bda0b1546a6a0e4b5f556295fc8299c272de5fbf"
+ ],
+ "24.13.1-linux_arm64": [
+ "node-v24.13.1-linux-arm64.tar.xz",
+ "node-v24.13.1-linux-arm64",
+ "c827d3d301e2eed1a51f36d0116b71b9e3d9e3b728f081615270ea40faac34c1"
+ ],
+ "24.13.1-linux_ppc64le": [
+ "node-v24.13.1-linux-ppc64le.tar.xz",
+ "node-v24.13.1-linux-ppc64le",
+ "fb712a08d317655dbf776c90f60ac2105109d802e33811df6c9ed33d12f801c6"
+ ],
+ "24.13.1-linux_s390x": [
+ "node-v24.13.1-linux-s390x.tar.xz",
+ "node-v24.13.1-linux-s390x",
+ "8e2c0d9b5545c3db22623e8cb8d6f0c28fcd470f29d32dbeabf9432dda289de2"
+ ],
+ "24.13.1-linux_amd64": [
+ "node-v24.13.1-linux-x64.tar.xz",
+ "node-v24.13.1-linux-x64",
+ "30215f90ea3cd04dfbc06e762c021393fa173a1d392974298bbc871a8e461089"
+ ],
+ "24.13.1-windows_amd64": [
+ "node-v24.13.1-win-x64.zip",
+ "node-v24.13.1-win-x64",
+ "fba577c4bb87df04d54dd87bbdaa5a2272f1f99a2acbf9152e1a91b8b5f0b279"
+ ]
+ },
"node_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
- "node_version": "24.0.0",
+ "node_version": "24.13.1",
"include_headers": false,
"platform": "linux_ppc64le"
}
@@ -1811,11 +1976,47 @@
"repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
"attributes": {
"node_download_auth": {},
- "node_repositories": {},
+ "node_repositories": {
+ "24.13.1-darwin_arm64": [
+ "node-v24.13.1-darwin-arm64.tar.gz",
+ "node-v24.13.1-darwin-arm64",
+ "8c039d59f2fec6195e4281ad5b0d02b9a940897b4df7b849c6fb48be6787bba6"
+ ],
+ "24.13.1-darwin_amd64": [
+ "node-v24.13.1-darwin-x64.tar.gz",
+ "node-v24.13.1-darwin-x64",
+ "527f0578d9812e7dfa225121bda0b1546a6a0e4b5f556295fc8299c272de5fbf"
+ ],
+ "24.13.1-linux_arm64": [
+ "node-v24.13.1-linux-arm64.tar.xz",
+ "node-v24.13.1-linux-arm64",
+ "c827d3d301e2eed1a51f36d0116b71b9e3d9e3b728f081615270ea40faac34c1"
+ ],
+ "24.13.1-linux_ppc64le": [
+ "node-v24.13.1-linux-ppc64le.tar.xz",
+ "node-v24.13.1-linux-ppc64le",
+ "fb712a08d317655dbf776c90f60ac2105109d802e33811df6c9ed33d12f801c6"
+ ],
+ "24.13.1-linux_s390x": [
+ "node-v24.13.1-linux-s390x.tar.xz",
+ "node-v24.13.1-linux-s390x",
+ "8e2c0d9b5545c3db22623e8cb8d6f0c28fcd470f29d32dbeabf9432dda289de2"
+ ],
+ "24.13.1-linux_amd64": [
+ "node-v24.13.1-linux-x64.tar.xz",
+ "node-v24.13.1-linux-x64",
+ "30215f90ea3cd04dfbc06e762c021393fa173a1d392974298bbc871a8e461089"
+ ],
+ "24.13.1-windows_amd64": [
+ "node-v24.13.1-win-x64.zip",
+ "node-v24.13.1-win-x64",
+ "fba577c4bb87df04d54dd87bbdaa5a2272f1f99a2acbf9152e1a91b8b5f0b279"
+ ]
+ },
"node_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
- "node_version": "24.0.0",
+ "node_version": "24.13.1",
"include_headers": false,
"platform": "darwin_amd64"
}
@@ -1824,11 +2025,47 @@
"repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
"attributes": {
"node_download_auth": {},
- "node_repositories": {},
+ "node_repositories": {
+ "24.13.1-darwin_arm64": [
+ "node-v24.13.1-darwin-arm64.tar.gz",
+ "node-v24.13.1-darwin-arm64",
+ "8c039d59f2fec6195e4281ad5b0d02b9a940897b4df7b849c6fb48be6787bba6"
+ ],
+ "24.13.1-darwin_amd64": [
+ "node-v24.13.1-darwin-x64.tar.gz",
+ "node-v24.13.1-darwin-x64",
+ "527f0578d9812e7dfa225121bda0b1546a6a0e4b5f556295fc8299c272de5fbf"
+ ],
+ "24.13.1-linux_arm64": [
+ "node-v24.13.1-linux-arm64.tar.xz",
+ "node-v24.13.1-linux-arm64",
+ "c827d3d301e2eed1a51f36d0116b71b9e3d9e3b728f081615270ea40faac34c1"
+ ],
+ "24.13.1-linux_ppc64le": [
+ "node-v24.13.1-linux-ppc64le.tar.xz",
+ "node-v24.13.1-linux-ppc64le",
+ "fb712a08d317655dbf776c90f60ac2105109d802e33811df6c9ed33d12f801c6"
+ ],
+ "24.13.1-linux_s390x": [
+ "node-v24.13.1-linux-s390x.tar.xz",
+ "node-v24.13.1-linux-s390x",
+ "8e2c0d9b5545c3db22623e8cb8d6f0c28fcd470f29d32dbeabf9432dda289de2"
+ ],
+ "24.13.1-linux_amd64": [
+ "node-v24.13.1-linux-x64.tar.xz",
+ "node-v24.13.1-linux-x64",
+ "30215f90ea3cd04dfbc06e762c021393fa173a1d392974298bbc871a8e461089"
+ ],
+ "24.13.1-windows_amd64": [
+ "node-v24.13.1-win-x64.zip",
+ "node-v24.13.1-win-x64",
+ "fba577c4bb87df04d54dd87bbdaa5a2272f1f99a2acbf9152e1a91b8b5f0b279"
+ ]
+ },
"node_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
- "node_version": "24.0.0",
+ "node_version": "24.13.1",
"include_headers": false,
"platform": "darwin_arm64"
}
@@ -1837,11 +2074,47 @@
"repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
"attributes": {
"node_download_auth": {},
- "node_repositories": {},
+ "node_repositories": {
+ "24.13.1-darwin_arm64": [
+ "node-v24.13.1-darwin-arm64.tar.gz",
+ "node-v24.13.1-darwin-arm64",
+ "8c039d59f2fec6195e4281ad5b0d02b9a940897b4df7b849c6fb48be6787bba6"
+ ],
+ "24.13.1-darwin_amd64": [
+ "node-v24.13.1-darwin-x64.tar.gz",
+ "node-v24.13.1-darwin-x64",
+ "527f0578d9812e7dfa225121bda0b1546a6a0e4b5f556295fc8299c272de5fbf"
+ ],
+ "24.13.1-linux_arm64": [
+ "node-v24.13.1-linux-arm64.tar.xz",
+ "node-v24.13.1-linux-arm64",
+ "c827d3d301e2eed1a51f36d0116b71b9e3d9e3b728f081615270ea40faac34c1"
+ ],
+ "24.13.1-linux_ppc64le": [
+ "node-v24.13.1-linux-ppc64le.tar.xz",
+ "node-v24.13.1-linux-ppc64le",
+ "fb712a08d317655dbf776c90f60ac2105109d802e33811df6c9ed33d12f801c6"
+ ],
+ "24.13.1-linux_s390x": [
+ "node-v24.13.1-linux-s390x.tar.xz",
+ "node-v24.13.1-linux-s390x",
+ "8e2c0d9b5545c3db22623e8cb8d6f0c28fcd470f29d32dbeabf9432dda289de2"
+ ],
+ "24.13.1-linux_amd64": [
+ "node-v24.13.1-linux-x64.tar.xz",
+ "node-v24.13.1-linux-x64",
+ "30215f90ea3cd04dfbc06e762c021393fa173a1d392974298bbc871a8e461089"
+ ],
+ "24.13.1-windows_amd64": [
+ "node-v24.13.1-win-x64.zip",
+ "node-v24.13.1-win-x64",
+ "fba577c4bb87df04d54dd87bbdaa5a2272f1f99a2acbf9152e1a91b8b5f0b279"
+ ]
+ },
"node_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
- "node_version": "24.0.0",
+ "node_version": "24.13.1",
"include_headers": false,
"platform": "windows_amd64"
}
@@ -1850,11 +2123,47 @@
"repoRuleId": "@@rules_nodejs+//nodejs:repositories.bzl%_nodejs_repositories",
"attributes": {
"node_download_auth": {},
- "node_repositories": {},
+ "node_repositories": {
+ "24.13.1-darwin_arm64": [
+ "node-v24.13.1-darwin-arm64.tar.gz",
+ "node-v24.13.1-darwin-arm64",
+ "8c039d59f2fec6195e4281ad5b0d02b9a940897b4df7b849c6fb48be6787bba6"
+ ],
+ "24.13.1-darwin_amd64": [
+ "node-v24.13.1-darwin-x64.tar.gz",
+ "node-v24.13.1-darwin-x64",
+ "527f0578d9812e7dfa225121bda0b1546a6a0e4b5f556295fc8299c272de5fbf"
+ ],
+ "24.13.1-linux_arm64": [
+ "node-v24.13.1-linux-arm64.tar.xz",
+ "node-v24.13.1-linux-arm64",
+ "c827d3d301e2eed1a51f36d0116b71b9e3d9e3b728f081615270ea40faac34c1"
+ ],
+ "24.13.1-linux_ppc64le": [
+ "node-v24.13.1-linux-ppc64le.tar.xz",
+ "node-v24.13.1-linux-ppc64le",
+ "fb712a08d317655dbf776c90f60ac2105109d802e33811df6c9ed33d12f801c6"
+ ],
+ "24.13.1-linux_s390x": [
+ "node-v24.13.1-linux-s390x.tar.xz",
+ "node-v24.13.1-linux-s390x",
+ "8e2c0d9b5545c3db22623e8cb8d6f0c28fcd470f29d32dbeabf9432dda289de2"
+ ],
+ "24.13.1-linux_amd64": [
+ "node-v24.13.1-linux-x64.tar.xz",
+ "node-v24.13.1-linux-x64",
+ "30215f90ea3cd04dfbc06e762c021393fa173a1d392974298bbc871a8e461089"
+ ],
+ "24.13.1-windows_amd64": [
+ "node-v24.13.1-win-x64.zip",
+ "node-v24.13.1-win-x64",
+ "fba577c4bb87df04d54dd87bbdaa5a2272f1f99a2acbf9152e1a91b8b5f0b279"
+ ]
+ },
"node_urls": [
"https://nodejs.org/dist/v{version}/{filename}"
],
- "node_version": "24.0.0",
+ "node_version": "24.13.1",
"include_headers": false,
"platform": "windows_arm64"
}
@@ -1883,7 +2192,7 @@
},
"@@rules_python+//python/extensions:pip.bzl%pip": {
"general": {
- "bzlTransitiveDigest": "d3ENjFH8qMwmOrkcb3c9JYqQ5hJ6owjfbSr24KY0Ugg=",
+ "bzlTransitiveDigest": "WViZ5k1A9F8R5wfEe2ArLMFS1g9UmgfbS8Q/7q1/z7o=",
"usagesDigest": "AK1R124YPWwAs8z1CQYyjYuci8RO5Ofot+EP5ZCNQDc=",
"recordedFileInputs": {
"@@protobuf+//python/requirements.txt": "983be60d3cec4b319dcab6d48aeb3f5b2f7c3350f26b3a9e97486c37967c73c5",
@@ -4621,7 +4930,7 @@
},
"@@rules_sass+//src/toolchain:extensions.bzl%sass": {
"general": {
- "bzlTransitiveDigest": "RA58Nyrsn03Z5YmQnpmBw3mqlVck++XIrx34amsqU/E=",
+ "bzlTransitiveDigest": "mOfuR8PsNuUWEq7JZ4MpIRbwyAGAqrCvkXXGaRNnlPQ=",
"usagesDigest": "R0KshhzIouLWuexMUCrl4HY+FUDwlVVgF9Z7UnwyUWA=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
@@ -4677,8 +4986,8 @@
},
"@@yq.bzl+//yq:extensions.bzl%yq": {
"general": {
- "bzlTransitiveDigest": "tDqk+ntWTdxNAWPDjRY1uITgHbti2jcXR5ZdinltBs0=",
- "usagesDigest": "OQwtwmKiZAvI0n0B86XlM4tmQHq4zcjFjAEiRGPhXVI=",
+ "bzlTransitiveDigest": "UfFMy8CWK4/dVo/tfaSAIYUiDGNAPes5eRllx9O9Q9Q=",
+ "usagesDigest": "da9wP1gnmr42ci1bT30rSLLWu7O0jJPIkd01GNAM7NE=",
"recordedFileInputs": {},
"recordedDirentsInputs": {},
"envVariables": {},
diff --git a/README.md b/README.md
index f14224e1cac7..9d3364263312 100644
--- a/README.md
+++ b/README.md
@@ -141,7 +141,6 @@ This is a monorepo which contains many tools and packages:
|---|---|---|---|
**Angular Build System** | [`@angular/build`](https://npmjs.com/package/@angular/build) | [](https://npmjs.com/package/@angular/build) | [](/packages/angular/build/README.md) [](https://github.com/angular/angular-build-builds)
**Angular CLI** | [`@angular/cli`](https://npmjs.com/package/@angular/cli) | [](https://npmjs.com/package/@angular/cli) | [](/packages/angular/cli/README.md) [](https://github.com/angular/cli-builds)
-**Architect CLI** | [`@angular-devkit/architect-cli`](https://npmjs.com/package/@angular-devkit/architect-cli) | [](https://npmjs.com/package/@angular-devkit/architect-cli) | [](https://github.com/angular/angular-devkit-architect-cli-builds)
**Schematics CLI** | [`@angular-devkit/schematics-cli`](https://npmjs.com/package/@angular-devkit/schematics-cli) | [](https://npmjs.com/package/@angular-devkit/schematics-cli) | [](https://github.com/angular/angular-devkit-schematics-cli-builds)
diff --git a/constants.bzl b/constants.bzl
index d4be4fc34b84..89d6eb63421c 100644
--- a/constants.bzl
+++ b/constants.bzl
@@ -1,12 +1,12 @@
# Engine versions to stamp in a release package.json
-RELEASE_ENGINES_NODE = "^20.19.0 || ^22.12.0 || >=24.0.0"
+RELEASE_ENGINES_NODE = "^22.22.0 || >=24.13.1"
RELEASE_ENGINES_NPM = "^6.11.0 || ^7.5.6 || >=8.0.0"
RELEASE_ENGINES_YARN = ">= 1.13.0"
-NG_PACKAGR_VERSION = "^21.2.0-next.0"
-ANGULAR_FW_VERSION = "^21.2.0-next.0"
-ANGULAR_FW_PEER_DEP = "^21.0.0 || ^21.2.0-next.0"
-NG_PACKAGR_PEER_DEP = "^21.0.0 || ^21.2.0-next.0"
+NG_PACKAGR_VERSION = "^22.0.0-next.0"
+ANGULAR_FW_VERSION = "^22.0.0-next.0"
+ANGULAR_FW_PEER_DEP = "^22.0.0-next.0"
+NG_PACKAGR_PEER_DEP = "^22.0.0-next.0"
# Baseline widely-available date in `YYYY-MM-DD` format which defines Angular's
# browser support. This date serves as the source of truth for the Angular CLI's
@@ -21,7 +21,6 @@ SNAPSHOT_REPOS = {
"@angular/build": "angular/angular-build-builds",
"@angular/ssr": "angular/angular-ssr-builds",
"@angular-devkit/architect": "angular/angular-devkit-architect-builds",
- "@angular-devkit/architect-cli": "angular/angular-devkit-architect-cli-builds",
"@angular-devkit/build-angular": "angular/angular-devkit-build-angular-builds",
"@angular-devkit/build-webpack": "angular/angular-devkit-build-webpack-builds",
"@angular-devkit/core": "angular/angular-devkit-core-builds",
diff --git a/docs/DEVELOPER.md b/docs/DEVELOPER.md
index 8204bd7dcbce..35b07adf1e89 100644
--- a/docs/DEVELOPER.md
+++ b/docs/DEVELOPER.md
@@ -56,7 +56,7 @@ project](#building-and-installing-the-cli), then run the desired `ng` command
as:
```shell
-node --inspect-brk node_modules/.bin/ng ...
+node --inspect-brk node_modules/.bin/ng
```
This will trigger a breakpoint as the CLI starts up. You can connect to this
@@ -168,7 +168,7 @@ Adding a package to this repository means running two separate commands:
1. `schematics devkit:package PACKAGE_NAME`. This will update the `.monorepo` file, and create the
base files for the new package (package.json, src/index, etc).
-1. `devkit-admin templates`. This will update the README and all other template files that might
+1. `pnpm admin templates`. This will update the README and all other template files that might
have changed when adding a new package.
For private packages, you will need to add a `"private": true` key to your package.json manually.
diff --git a/eslint.config.mjs b/eslint.config.mjs
index 192ba19f2007..08a993804c32 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -10,9 +10,9 @@ import { fixupConfigRules, fixupPluginRules } from '@eslint/compat';
import { FlatCompat } from '@eslint/eslintrc';
import js from '@eslint/js';
import stylistic from '@stylistic/eslint-plugin';
+import header from '@tony.ganchev/eslint-plugin-header';
import typescriptEslint from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
-import header from 'eslint-plugin-header';
import _import from 'eslint-plugin-import';
import globals from 'globals';
@@ -22,12 +22,15 @@ const compat = new FlatCompat({
allConfig: js.configs.all,
});
-// See: https://github.com/Stuk/eslint-plugin-header/issues/57
-header.rules.header.meta.schema = false;
-
export default [
+ {
+ files: ['**/*.ts', '**/*.mts', '**/*.cts'],
+ },
{
ignores: [
+ '**/*.js',
+ '**/*.mjs',
+ '**/*.cjs',
'**/bazel-out',
'**/dist-schema',
'goldens/public-api',
@@ -59,7 +62,7 @@ export default [
'@stylistic': stylistic,
'@typescript-eslint': fixupPluginRules(typescriptEslint),
import: fixupPluginRules(_import),
- header,
+ '@tony.ganchev': header,
},
languageOptions: {
@@ -72,7 +75,8 @@ export default [
sourceType: 'module',
parserOptions: {
- project: 'tsconfig.json',
+ projectService: true, // v8 recommended alternative to "project"
+ tsconfigRootDir: import.meta.dirname,
},
},
@@ -106,7 +110,7 @@ export default [
'@typescript-eslint/no-unused-expressions': 'error',
curly: 'error',
- 'header/header': [
+ '@tony.ganchev/header': [
'error',
'block',
[
diff --git a/goldens/BUILD.bazel b/goldens/BUILD.bazel
index 6dbbdd28f25b..711154f524b1 100644
--- a/goldens/BUILD.bazel
+++ b/goldens/BUILD.bazel
@@ -1,4 +1,4 @@
-load("@aspect_bazel_lib//lib:copy_to_bin.bzl", "copy_to_bin")
+load("@bazel_lib//lib:copy_to_bin.bzl", "copy_to_bin")
package(default_visibility = ["//visibility:public"])
diff --git a/goldens/public-api/angular/ssr/index.api.md b/goldens/public-api/angular/ssr/index.api.md
index 81764fcc1f62..e5d85138b72f 100644
--- a/goldens/public-api/angular/ssr/index.api.md
+++ b/goldens/public-api/angular/ssr/index.api.md
@@ -6,19 +6,30 @@
import { DefaultExport } from '@angular/router';
import { EnvironmentProviders } from '@angular/core';
+import { InjectionToken } from '@angular/core';
import { Provider } from '@angular/core';
import { Type } from '@angular/core';
// @public
export class AngularAppEngine {
+ constructor(options?: AngularAppEngineOptions);
handle(request: Request, requestContext?: unknown): Promise;
static ɵallowStaticRouteRender: boolean;
+ static ɵdisableAllowedHostsCheck: boolean;
static ɵhooks: Hooks;
}
+// @public
+export interface AngularAppEngineOptions {
+ allowedHosts?: readonly string[];
+}
+
// @public
export function createRequestHandler(handler: RequestHandlerFunction): RequestHandlerFunction;
+// @public
+export const IS_DISCOVERING_ROUTES: InjectionToken;
+
// @public
export enum PrerenderFallback {
Client = 1,
diff --git a/goldens/public-api/angular/ssr/node/index.api.md b/goldens/public-api/angular/ssr/node/index.api.md
index eccb6396938e..2c52c06b47c1 100644
--- a/goldens/public-api/angular/ssr/node/index.api.md
+++ b/goldens/public-api/angular/ssr/node/index.api.md
@@ -15,8 +15,12 @@ import { Type } from '@angular/core';
// @public
export class AngularNodeAppEngine {
- constructor();
- handle(request: IncomingMessage | Http2ServerRequest, requestContext?: unknown): Promise;
+ constructor(options?: AngularNodeAppEngineOptions);
+ handle(request: IncomingMessage | Http2ServerRequest | Request, requestContext?: unknown): Promise;
+}
+
+// @public
+export interface AngularNodeAppEngineOptions extends AngularAppEngineOptions {
}
// @public
@@ -27,6 +31,7 @@ export class CommonEngine {
// @public (undocumented)
export interface CommonEngineOptions {
+ allowedHosts?: readonly string[];
bootstrap?: Type<{}> | ((context: BootstrapContext) => Promise);
enablePerformanceProfiler?: boolean;
providers?: StaticProvider[];
diff --git a/goldens/public-api/angular_devkit/architect/index.api.md b/goldens/public-api/angular_devkit/architect/index.api.md
index 0ae8751719b5..747b7010f580 100644
--- a/goldens/public-api/angular_devkit/architect/index.api.md
+++ b/goldens/public-api/angular_devkit/architect/index.api.md
@@ -530,7 +530,7 @@ export type Target = json.JsonObject & Target_2;
export function targetFromTargetString(specifier: string, abbreviatedProjectName?: string, abbreviatedTargetName?: string): Target;
// @public
-export function targetStringFromTarget({ project, target, configuration }: Target): string;
+export function targetStringFromTarget(input: Target): string;
// @public
export type TypedBuilderProgress = {
diff --git a/goldens/public-api/angular_devkit/build_angular/index.api.md b/goldens/public-api/angular_devkit/build_angular/index.api.md
index 0208e998a300..a7747bda9da2 100644
--- a/goldens/public-api/angular_devkit/build_angular/index.api.md
+++ b/goldens/public-api/angular_devkit/build_angular/index.api.md
@@ -173,9 +173,6 @@ export function executeKarmaBuilder(options: KarmaBuilderOptions, context: Build
// @public
export function executeNgPackagrBuilder(options: NgPackagrBuilderOptions, context: BuilderContext): Observable;
-// @public
-export function executeProtractorBuilder(options: ProtractorBuilderOptions, context: BuilderContext): Promise;
-
// @public
export function executeServerBuilder(options: ServerBuilderOptions, context: BuilderContext, transforms?: {
webpackConfiguration?: ExecutionTransformer;
@@ -269,20 +266,6 @@ export enum OutputHashing {
None = "none"
}
-// @public
-export type ProtractorBuilderOptions = {
- baseUrl?: string;
- devServerTarget?: string;
- grep?: string;
- host?: string;
- invertGrep?: boolean;
- port?: number;
- protractorConfig: string;
- specs?: string[];
- suite?: string;
- webdriverUpdate?: boolean;
-};
-
// @public (undocumented)
export type ServerBuilderOptions = {
assets?: AssetPattern_3[];
diff --git a/modules/testing/builder/BUILD.bazel b/modules/testing/builder/BUILD.bazel
index 7f542efb0138..45c39358c29c 100644
--- a/modules/testing/builder/BUILD.bazel
+++ b/modules/testing/builder/BUILD.bazel
@@ -21,7 +21,9 @@ ts_project(
# resolvable in the test project.
":node_modules/@angular/ssr",
":node_modules/browser-sync",
+ ":node_modules/istanbul-lib-instrument",
":node_modules/jsdom",
+ ":node_modules/ng-packagr",
":node_modules/vitest",
":node_modules/@vitest/coverage-v8",
] + glob(["projects/**/*"]),
diff --git a/modules/testing/builder/package.json b/modules/testing/builder/package.json
index 47ff33d0cd7d..5ba604719d42 100644
--- a/modules/testing/builder/package.json
+++ b/modules/testing/builder/package.json
@@ -1,13 +1,15 @@
{
"devDependencies": {
- "@angular-devkit/core": "workspace:*",
"@angular-devkit/architect": "workspace:*",
- "@angular/ssr": "workspace:*",
"@angular-devkit/build-angular": "workspace:*",
+ "@angular-devkit/core": "workspace:*",
+ "@angular/ssr": "workspace:*",
+ "@vitest/coverage-v8": "4.1.2",
"browser-sync": "3.0.4",
- "@vitest/coverage-v8": "4.0.18",
- "jsdom": "28.1.0",
+ "istanbul-lib-instrument": "6.0.3",
+ "jsdom": "29.0.1",
+ "ng-packagr": "22.0.0-next.1",
"rxjs": "7.8.2",
- "vitest": "4.0.18"
+ "vitest": "4.1.2"
}
}
diff --git a/modules/testing/builder/projects/hello-world-app/README.md b/modules/testing/builder/projects/hello-world-app/README.md
index 3c578babc961..c2795c8bcc94 100644
--- a/modules/testing/builder/projects/hello-world-app/README.md
+++ b/modules/testing/builder/projects/hello-world-app/README.md
@@ -20,7 +20,7 @@ Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.
## Running end-to-end tests
-Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
+Run `ng e2e` to setup and run the end-to-end tests.
## Further help
diff --git a/modules/testing/builder/projects/hello-world-app/angular.json b/modules/testing/builder/projects/hello-world-app/angular.json
index 95607701be8f..9ab612b8b2c5 100644
--- a/modules/testing/builder/projects/hello-world-app/angular.json
+++ b/modules/testing/builder/projects/hello-world-app/angular.json
@@ -172,20 +172,6 @@
}
}
}
- },
- "app-e2e": {
- "root": "e2e",
- "projectType": "application",
- "targets": {
- "e2e": {
- "builder": "@angular-devkit/build-angular:private-protractor",
- "options": {
- "protractorConfig": "protractor.conf.js",
- "devServerTarget": "app:serve",
- "webdriverUpdate": false
- }
- }
- }
}
}
}
diff --git a/modules/testing/builder/projects/hello-world-app/e2e/app.e2e-spec.ts b/modules/testing/builder/projects/hello-world-app/e2e/app.e2e-spec.ts
deleted file mode 100644
index 862e95302ce3..000000000000
--- a/modules/testing/builder/projects/hello-world-app/e2e/app.e2e-spec.ts
+++ /dev/null
@@ -1,22 +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 { AppPage } from './app.po';
-
-describe('hello-world-app App', () => {
- let page: AppPage;
-
- beforeEach(() => {
- page = new AppPage();
- });
-
- it('should display welcome message', async () => {
- page.navigateTo();
- expect(await page.getTitleText()).toEqual('Welcome to app!');
- });
-});
diff --git a/modules/testing/builder/projects/hello-world-app/e2e/app.po.ts b/modules/testing/builder/projects/hello-world-app/e2e/app.po.ts
deleted file mode 100644
index 7cb6bc3fb743..000000000000
--- a/modules/testing/builder/projects/hello-world-app/e2e/app.po.ts
+++ /dev/null
@@ -1,19 +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 { browser, by, element } from 'protractor';
-
-export class AppPage {
- navigateTo(): Promise {
- return browser.get(browser.baseUrl) as Promise;
- }
-
- getTitleText(): Promise {
- return element(by.css('app-root h1')).getText() as Promise;
- }
-}
diff --git a/modules/testing/builder/projects/hello-world-app/e2e/tsconfig.e2e.json b/modules/testing/builder/projects/hello-world-app/e2e/tsconfig.e2e.json
deleted file mode 100644
index a82df00eef37..000000000000
--- a/modules/testing/builder/projects/hello-world-app/e2e/tsconfig.e2e.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "extends": "../tsconfig.json",
- "compilerOptions": {
- "outDir": "../out-tsc/e2e",
- "module": "commonjs",
- "target": "es2018",
- "types": [
- "jasmine",
- "node"
- ]
- }
-}
diff --git a/modules/testing/builder/projects/hello-world-app/karma.conf.js b/modules/testing/builder/projects/hello-world-app/karma.conf.js
index 7d5f7c8d98f5..d3206bd4cf87 100644
--- a/modules/testing/builder/projects/hello-world-app/karma.conf.js
+++ b/modules/testing/builder/projects/hello-world-app/karma.conf.js
@@ -10,7 +10,10 @@
// https://karma-runner.github.io/1.0/config/configuration-file.html
const path = require('path');
-process.env.CHROME_BIN = require('puppeteer').executablePath();
+
+// Resolve from relative paths to absolute paths within the bazel runfiles tree
+// so subprocesses spawned in a different working directory can still find them.
+process.env.CHROME_BIN = process.env.CHROME_BIN ? path.resolve(path.join(process.cwd(), '..', '..', '..'), process.env.CHROME_BIN) : require('puppeteer').executablePath();
module.exports = function(config) {
config.set({
diff --git a/modules/testing/builder/projects/hello-world-app/protractor.conf.js b/modules/testing/builder/projects/hello-world-app/protractor.conf.js
deleted file mode 100644
index 313f7ac7c53b..000000000000
--- a/modules/testing/builder/projects/hello-world-app/protractor.conf.js
+++ /dev/null
@@ -1,43 +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
- */
-
-// Protractor configuration file, see link for more information
-// https://github.com/angular/protractor/blob/master/lib/config.ts
-
-const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
-const { resolve } = require('path');
-
-exports.config = {
- allScriptsTimeout: 11000,
- specs: ['./e2e/**/*.e2e-spec.ts'],
- capabilities: {
- browserName: 'chrome',
- chromeOptions: {
- args: ['--headless', '--no-sandbox', '--disable-gpu', '--disable-dev-shm-usage'],
- binary: require('puppeteer').executablePath(),
- },
- },
- directConnect: true,
- baseUrl: 'http://localhost:4200/',
- framework: 'jasmine',
- jasmineNodeOpts: {
- showColors: true,
- defaultTimeoutInterval: 30000,
- print: function() {},
- },
- onPrepare() {
- require('ts-node').register({
- project: resolve(__dirname, './e2e/tsconfig.e2e.json'),
- });
- jasmine.getEnv().addReporter(new SpecReporter({
- spec: {
- displayStacktrace: StacktraceOption.PRETTY
- }
- }));
- },
-};
diff --git a/modules/testing/builder/projects/hello-world-app/src/tsconfig.server.json b/modules/testing/builder/projects/hello-world-app/src/tsconfig.server.json
index 9a492c781eb1..3fd4df49c45d 100644
--- a/modules/testing/builder/projects/hello-world-app/src/tsconfig.server.json
+++ b/modules/testing/builder/projects/hello-world-app/src/tsconfig.server.json
@@ -2,8 +2,7 @@
"extends": "./tsconfig.app.json",
"compilerOptions": {
"outDir": "../dist-server",
- "baseUrl": "./",
- "types": ["@angular/localize", "node"]
+ "types": ["@angular/localize", "node", "@types/express"]
},
"files": [
"main.server.ts"
diff --git a/modules/testing/builder/projects/hello-world-app/tsconfig.json b/modules/testing/builder/projects/hello-world-app/tsconfig.json
index 8f8859a21d4f..4f9890cb8e7d 100644
--- a/modules/testing/builder/projects/hello-world-app/tsconfig.json
+++ b/modules/testing/builder/projects/hello-world-app/tsconfig.json
@@ -1,7 +1,6 @@
{
"compileOnSave": false,
"compilerOptions": {
- "baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
diff --git a/packages/angular_devkit/build_angular/test/hello-world-lib/.gitignore b/modules/testing/builder/projects/hello-world-lib/.gitignore
similarity index 100%
rename from packages/angular_devkit/build_angular/test/hello-world-lib/.gitignore
rename to modules/testing/builder/projects/hello-world-lib/.gitignore
diff --git a/packages/angular_devkit/build_angular/test/hello-world-lib/angular.json b/modules/testing/builder/projects/hello-world-lib/angular.json
similarity index 100%
rename from packages/angular_devkit/build_angular/test/hello-world-lib/angular.json
rename to modules/testing/builder/projects/hello-world-lib/angular.json
diff --git a/packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/karma.conf.js b/modules/testing/builder/projects/hello-world-lib/projects/lib/karma.conf.js
similarity index 100%
rename from packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/karma.conf.js
rename to modules/testing/builder/projects/hello-world-lib/projects/lib/karma.conf.js
diff --git a/packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/ng-package.json b/modules/testing/builder/projects/hello-world-lib/projects/lib/ng-package.json
similarity index 100%
rename from packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/ng-package.json
rename to modules/testing/builder/projects/hello-world-lib/projects/lib/ng-package.json
diff --git a/modules/testing/builder/projects/hello-world-lib/projects/lib/package.json b/modules/testing/builder/projects/hello-world-lib/projects/lib/package.json
new file mode 100644
index 000000000000..ac7b847f2366
--- /dev/null
+++ b/modules/testing/builder/projects/hello-world-lib/projects/lib/package.json
@@ -0,0 +1,8 @@
+{
+ "name": "lib",
+ "version": "0.0.1",
+ "peerDependencies": {
+ "@angular/common": "^22.0.0-next",
+ "@angular/core": "^22.0.0-next"
+ }
+}
\ No newline at end of file
diff --git a/packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/src/lib/lib.component.spec.ts b/modules/testing/builder/projects/hello-world-lib/projects/lib/src/lib/lib.component.spec.ts
similarity index 100%
rename from packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/src/lib/lib.component.spec.ts
rename to modules/testing/builder/projects/hello-world-lib/projects/lib/src/lib/lib.component.spec.ts
diff --git a/packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/src/lib/lib.component.ts b/modules/testing/builder/projects/hello-world-lib/projects/lib/src/lib/lib.component.ts
similarity index 100%
rename from packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/src/lib/lib.component.ts
rename to modules/testing/builder/projects/hello-world-lib/projects/lib/src/lib/lib.component.ts
diff --git a/packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/src/lib/lib.service.spec.ts b/modules/testing/builder/projects/hello-world-lib/projects/lib/src/lib/lib.service.spec.ts
similarity index 100%
rename from packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/src/lib/lib.service.spec.ts
rename to modules/testing/builder/projects/hello-world-lib/projects/lib/src/lib/lib.service.spec.ts
diff --git a/packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/src/lib/lib.service.ts b/modules/testing/builder/projects/hello-world-lib/projects/lib/src/lib/lib.service.ts
similarity index 100%
rename from packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/src/lib/lib.service.ts
rename to modules/testing/builder/projects/hello-world-lib/projects/lib/src/lib/lib.service.ts
diff --git a/packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/src/public-api.ts b/modules/testing/builder/projects/hello-world-lib/projects/lib/src/public-api.ts
similarity index 100%
rename from packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/src/public-api.ts
rename to modules/testing/builder/projects/hello-world-lib/projects/lib/src/public-api.ts
diff --git a/packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/tsconfig.lib.json b/modules/testing/builder/projects/hello-world-lib/projects/lib/tsconfig.lib.json
similarity index 100%
rename from packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/tsconfig.lib.json
rename to modules/testing/builder/projects/hello-world-lib/projects/lib/tsconfig.lib.json
diff --git a/packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/tsconfig.lib.prod.json b/modules/testing/builder/projects/hello-world-lib/projects/lib/tsconfig.lib.prod.json
similarity index 100%
rename from packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/tsconfig.lib.prod.json
rename to modules/testing/builder/projects/hello-world-lib/projects/lib/tsconfig.lib.prod.json
diff --git a/packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/tsconfig.spec.json b/modules/testing/builder/projects/hello-world-lib/projects/lib/tsconfig.spec.json
similarity index 100%
rename from packages/angular_devkit/build_angular/test/hello-world-lib/projects/lib/tsconfig.spec.json
rename to modules/testing/builder/projects/hello-world-lib/projects/lib/tsconfig.spec.json
diff --git a/packages/angular_devkit/build_angular/test/hello-world-lib/tsconfig.json b/modules/testing/builder/projects/hello-world-lib/tsconfig.json
similarity index 95%
rename from packages/angular_devkit/build_angular/test/hello-world-lib/tsconfig.json
rename to modules/testing/builder/projects/hello-world-lib/tsconfig.json
index f749045924e4..8ccae14cd594 100644
--- a/packages/angular_devkit/build_angular/test/hello-world-lib/tsconfig.json
+++ b/modules/testing/builder/projects/hello-world-lib/tsconfig.json
@@ -1,7 +1,6 @@
{
"compileOnSave": false,
"compilerOptions": {
- "baseUrl": "./",
"outDir": "./dist/out-tsc",
"sourceMap": true,
"declaration": false,
diff --git a/modules/testing/builder/src/jasmine-helpers.ts b/modules/testing/builder/src/jasmine-helpers.ts
index eb48f8b8dbbc..bd1943ae3ea1 100644
--- a/modules/testing/builder/src/jasmine-helpers.ts
+++ b/modules/testing/builder/src/jasmine-helpers.ts
@@ -25,6 +25,8 @@ export const BUILD_TIMEOUT = 30_000;
const optionSchemaCache = new Map();
+let counter = 0;
+
export function describeBuilder(
builderHandler: BuilderHandlerFn,
options: { name?: string; schemaPath: string },
@@ -40,10 +42,8 @@ export function describeBuilder(
optionSchema,
});
- // This is needed as there are multiple describe calls for the same builder.
- jasmine.getEnv().configure({ forbidDuplicateNames: false });
-
- describe(options.name || builderHandler.name, () => {
+ // The counter is needed to avoid duplicate describe names as they are not allowed.
+ describe((options.name || builderHandler.name) + ` (Suite: ${counter++})`, () => {
beforeEach(async () => {
harness.resetProjectMetadata();
diff --git a/modules/testing/builder/src/test-utils.ts b/modules/testing/builder/src/test-utils.ts
index f41cb42d1ea6..63ded0215c90 100644
--- a/modules/testing/builder/src/test-utils.ts
+++ b/modules/testing/builder/src/test-utils.ts
@@ -36,7 +36,6 @@ export const devServerTargetSpec = { project: 'app', target: 'serve' };
export const extractI18nTargetSpec = { project: 'app', target: 'extract-i18n' };
export const karmaTargetSpec = { project: 'app', target: 'test' };
export const tslintTargetSpec = { project: 'app', target: 'lint' };
-export const protractorTargetSpec = { project: 'app-e2e', target: 'e2e' };
export async function createArchitect(workspaceRoot: Path) {
const registry = new schema.CoreSchemaRegistry();
@@ -81,7 +80,7 @@ export async function browserBuild(
scheduleOptions?: ScheduleOptions,
): Promise {
const run = await architect.scheduleTarget(target, overrides, scheduleOptions);
- const output = (await run.result) as BuilderOutput & { outputs: { path: string }[] };
+ const output = await run.result;
expect(output.success).toBe(true);
if (!output.success) {
diff --git a/package.json b/package.json
index 39fba551b317..36467b1a21f9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@angular/devkit-repo",
- "version": "21.2.0-next.2",
+ "version": "22.0.0-next.5",
"private": true,
"description": "Software Development Kit for Angular",
"keywords": [
@@ -16,7 +16,7 @@
"test": "bazel test //packages/...",
"build": "pnpm -s admin build",
"build-schema": "bazel build //... --build_tag_filters schema --symlink_prefix dist-schema/",
- "lint": "eslint --cache --max-warnings=0 \"**/*.@(ts|mts|cts)\"",
+ "lint": "eslint --cache --max-warnings=0",
"templates": "pnpm -s admin templates",
"validate": "pnpm -s admin validate",
"postinstall": "husky",
@@ -26,14 +26,14 @@
},
"repository": {
"type": "git",
- "url": "https://github.com/angular/angular-cli.git"
+ "url": "git+https://github.com/angular/angular-cli.git"
},
- "packageManager": "pnpm@10.29.3",
+ "packageManager": "pnpm@10.33.0",
"engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
+ "node": "^22.22.0 || >=24.13.1",
"npm": "Please use pnpm instead of NPM to install dependencies",
"yarn": "Please use pnpm instead of Yarn to install dependencies",
- "pnpm": "10.29.3"
+ "pnpm": "10.33.0"
},
"author": "Angular Authors",
"license": "MIT",
@@ -42,38 +42,41 @@
},
"homepage": "https://github.com/angular/angular-cli",
"devDependencies": {
- "@angular/animations": "21.2.0-next.3",
- "@angular/cdk": "21.2.0-next.4",
- "@angular/common": "21.2.0-next.3",
- "@angular/compiler": "21.2.0-next.3",
- "@angular/compiler-cli": "21.2.0-next.3",
- "@angular/core": "21.2.0-next.3",
- "@angular/forms": "21.2.0-next.3",
- "@angular/localize": "21.2.0-next.3",
- "@angular/material": "21.2.0-next.4",
- "@angular/ng-dev": "https://github.com/angular/dev-infra-private-ng-dev-builds.git#2a5e8e5b5398ae13a8d9a963ac980707313a6c9e",
- "@angular/platform-browser": "21.2.0-next.3",
- "@angular/platform-server": "21.2.0-next.3",
- "@angular/router": "21.2.0-next.3",
- "@angular/service-worker": "21.2.0-next.3",
+ "@angular/animations": "22.0.0-next.6",
+ "@angular/cdk": "22.0.0-next.3",
+ "@angular/common": "22.0.0-next.6",
+ "@angular/compiler": "22.0.0-next.6",
+ "@angular/compiler-cli": "22.0.0-next.6",
+ "@angular/core": "22.0.0-next.6",
+ "@angular/forms": "22.0.0-next.6",
+ "@angular/localize": "22.0.0-next.6",
+ "@angular/material": "22.0.0-next.3",
+ "@angular/ng-dev": "https://github.com/angular/dev-infra-private-ng-dev-builds.git#1c95e84f330960eea28b5e3ed9b7a6a21b2c605b",
+ "@angular/platform-browser": "22.0.0-next.6",
+ "@angular/platform-server": "22.0.0-next.6",
+ "@angular/router": "22.0.0-next.6",
+ "@angular/service-worker": "22.0.0-next.6",
"@babel/core": "7.29.0",
"@bazel/bazelisk": "1.28.1",
"@bazel/buildifier": "8.2.1",
- "@eslint/compat": "2.0.2",
- "@eslint/eslintrc": "3.3.3",
- "@eslint/js": "9.39.2",
+ "@bazel/ibazel": "^0.28.0",
+ "@eslint/compat": "2.0.3",
+ "@eslint/eslintrc": "3.3.5",
+ "@eslint/js": "10.0.1",
"@rollup/plugin-alias": "^6.0.0",
"@rollup/plugin-commonjs": "^29.0.0",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "16.0.3",
+ "@rollup/wasm-node": "4.60.0",
"@stylistic/eslint-plugin": "^5.0.0",
+ "@tony.ganchev/eslint-plugin-header": "~3.3.1",
"@types/babel__core": "7.20.5",
"@types/babel__generator": "^7.6.8",
"@types/browser-sync": "^2.27.0",
"@types/express": "~5.0.1",
"@types/http-proxy": "^1.17.4",
"@types/ini": "^4.0.0",
- "@types/jasmine": "~5.1.0",
+ "@types/jasmine": "~6.0.0",
"@types/jasmine-reporters": "^2",
"@types/karma": "^6.3.0",
"@types/less": "^3.0.3",
@@ -89,24 +92,23 @@
"@types/yargs": "^17.0.20",
"@types/yargs-parser": "^21.0.0",
"@types/yarnpkg__lockfile": "^1.1.5",
- "@typescript-eslint/eslint-plugin": "8.55.0",
- "@typescript-eslint/parser": "8.55.0",
+ "@typescript-eslint/eslint-plugin": "8.57.2",
+ "@typescript-eslint/parser": "8.57.2",
"ajv": "8.18.0",
"buffer": "6.0.3",
"esbuild": "0.27.3",
"esbuild-wasm": "0.27.3",
- "eslint": "9.39.2",
+ "eslint": "10.1.0",
"eslint-config-prettier": "10.1.8",
- "eslint-plugin-header": "3.1.1",
"eslint-plugin-import": "2.32.0",
"express": "5.2.1",
"fast-glob": "3.3.3",
- "globals": "17.3.0",
+ "globals": "17.4.0",
"http-proxy": "^1.18.1",
"http-proxy-middleware": "3.0.5",
"husky": "9.1.7",
- "jasmine": "~5.13.0",
- "jasmine-core": "~5.13.0",
+ "jasmine": "~6.1.0",
+ "jasmine-core": "~6.1.0",
"jasmine-reporters": "^2.5.2",
"jasmine-spec-reporter": "~7.0.0",
"karma": "~6.4.0",
@@ -118,21 +120,19 @@
"lodash": "^4.17.21",
"magic-string": "0.30.21",
"prettier": "^3.0.0",
- "protractor": "~7.0.0",
- "puppeteer": "18.2.1",
+ "puppeteer": "24.40.0",
"quicktype-core": "23.2.6",
- "rollup": "4.57.1",
- "rollup-license-plugin": "~3.1.0",
- "rollup-plugin-dts": "6.3.0",
- "rollup-plugin-sourcemaps2": "0.5.4",
+ "rollup": "4.60.0",
+ "rollup-license-plugin": "~3.2.0",
+ "rollup-plugin-dts": "6.4.1",
+ "rollup-plugin-sourcemaps2": "0.5.6",
"semver": "7.7.4",
"source-map-support": "0.5.21",
- "ts-node": "^10.9.1",
"tslib": "2.8.1",
- "typescript": "5.9.3",
- "undici": "7.22.0",
+ "typescript": "6.0.2",
+ "undici": "7.24.6",
"unenv": "^1.10.0",
- "verdaccio": "6.2.5",
+ "verdaccio": "6.3.2",
"verdaccio-auth-memory": "^10.0.0",
"zone.js": "^0.16.0"
},
@@ -146,7 +146,6 @@
},
"pnpm": {
"onlyBuiltDependencies": [
- "puppeteer",
"webdriver-manager"
],
"overrides": {
@@ -157,6 +156,11 @@
"peerDependencies": {
"protobufjs": "*"
}
+ },
+ "vitest": {
+ "peerDependencies": {
+ "@vitest/coverage-v8": "*"
+ }
}
}
},
diff --git a/packages/angular/build/BUILD.bazel b/packages/angular/build/BUILD.bazel
index 2f74117ff850..5303f6763020 100644
--- a/packages/angular/build/BUILD.bazel
+++ b/packages/angular/build/BUILD.bazel
@@ -105,7 +105,6 @@ ts_project(
":node_modules/sass",
":node_modules/source-map-support",
":node_modules/tinyglobby",
- ":node_modules/undici",
":node_modules/vite",
":node_modules/vitest",
":node_modules/watchpack",
diff --git a/packages/angular/build/package.json b/packages/angular/build/package.json
index c4d847ae5a8e..b0d4a1197d74 100644
--- a/packages/angular/build/package.json
+++ b/packages/angular/build/package.json
@@ -23,41 +23,40 @@
"@babel/core": "7.29.0",
"@babel/helper-annotate-as-pure": "7.27.3",
"@babel/helper-split-export-declaration": "7.24.7",
- "@inquirer/confirm": "5.1.21",
- "@vitejs/plugin-basic-ssl": "2.1.4",
+ "@inquirer/confirm": "6.0.10",
+ "@vitejs/plugin-basic-ssl": "2.3.0",
"beasties": "0.4.1",
"browserslist": "^4.26.0",
"esbuild": "0.27.3",
- "https-proxy-agent": "7.0.6",
- "istanbul-lib-instrument": "6.0.3",
+ "https-proxy-agent": "9.0.0",
"jsonc-parser": "3.3.1",
- "listr2": "9.0.5",
+ "listr2": "10.2.1",
"magic-string": "0.30.21",
"mrmime": "2.0.1",
"parse5-html-rewriting-stream": "8.0.0",
- "picomatch": "4.0.3",
+ "picomatch": "4.0.4",
"piscina": "5.1.4",
- "rolldown": "1.0.0-rc.4",
- "sass": "1.97.3",
+ "rolldown": "1.0.0-rc.12",
+ "sass": "1.98.0",
"semver": "7.7.4",
"source-map-support": "0.5.21",
"tinyglobby": "0.2.15",
- "undici": "7.22.0",
"vite": "7.3.1",
"watchpack": "2.5.1"
},
"optionalDependencies": {
- "lmdb": "3.5.1"
+ "lmdb": "3.5.2"
},
"devDependencies": {
"@angular-devkit/core": "workspace:*",
"@angular/ssr": "workspace:*",
- "jsdom": "28.1.0",
- "less": "4.4.2",
- "ng-packagr": "21.2.0-next.0",
- "postcss": "8.5.6",
+ "istanbul-lib-instrument": "6.0.3",
+ "jsdom": "29.0.1",
+ "less": "4.6.4",
+ "ng-packagr": "22.0.0-next.1",
+ "postcss": "8.5.8",
"rxjs": "7.8.2",
- "vitest": "4.0.18"
+ "vitest": "4.1.2"
},
"peerDependencies": {
"@angular/compiler": "0.0.0-ANGULAR-FW-PEER-DEP",
@@ -68,13 +67,14 @@
"@angular/platform-server": "0.0.0-ANGULAR-FW-PEER-DEP",
"@angular/service-worker": "0.0.0-ANGULAR-FW-PEER-DEP",
"@angular/ssr": "^0.0.0-PLACEHOLDER",
+ "istanbul-lib-instrument": "^6.0.0",
"karma": "^6.4.0",
"less": "^4.2.0",
"ng-packagr": "0.0.0-NG-PACKAGR-PEER-DEP",
"postcss": "^8.4.0",
"tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0",
"tslib": "^2.3.0",
- "typescript": ">=5.9 <6.0",
+ "typescript": ">=5.9 <6.1",
"vitest": "^4.0.8"
},
"peerDependenciesMeta": {
@@ -96,6 +96,9 @@
"@angular/ssr": {
"optional": true
},
+ "istanbul-lib-instrument": {
+ "optional": true
+ },
"karma": {
"optional": true
},
diff --git a/packages/angular/build/src/builders/application/execute-build.ts b/packages/angular/build/src/builders/application/execute-build.ts
index 0654cd965558..aaddc5b6ef7e 100644
--- a/packages/angular/build/src/builders/application/execute-build.ts
+++ b/packages/angular/build/src/builders/application/execute-build.ts
@@ -56,6 +56,7 @@ export async function executeBuild(
verbose,
colors,
jsonLogs,
+ security,
} = options;
// TODO: Consider integrating into watch mode. Would require full rebuild on target changes.
@@ -263,7 +264,7 @@ export async function executeBuild(
if (serverEntryPoint) {
executionResult.addOutputFile(
SERVER_APP_ENGINE_MANIFEST_FILENAME,
- generateAngularServerAppEngineManifest(i18nOptions, baseHref),
+ generateAngularServerAppEngineManifest(i18nOptions, security.allowedHosts, baseHref),
BuildOutputFileType.ServerRoot,
);
}
diff --git a/packages/angular/build/src/builders/application/i18n.ts b/packages/angular/build/src/builders/application/i18n.ts
index ae37efa674e4..081be50e7a9f 100644
--- a/packages/angular/build/src/builders/application/i18n.ts
+++ b/packages/angular/build/src/builders/application/i18n.ts
@@ -123,48 +123,48 @@ export async function inlineI18n(
inlineResult.prerenderedRoutes = { ...inlineResult.prerenderedRoutes, ...generatedRoutes };
updatedOutputFiles.push(...localeOutputFiles);
}
- } finally {
- await inliner.close();
- }
- // Update the result with all localized files.
- executionResult.outputFiles = [
- // Root and SSR entry files are not modified.
- ...unModifiedOutputFiles,
- // Updated files for each locale.
- ...updatedOutputFiles,
- ];
-
- // Assets are only changed if not using the flat output option
- if (!i18nOptions.flatOutput) {
- executionResult.assetFiles = updatedAssetFiles;
- }
-
- // Inline any template updates if present
- if (executionResult.templateUpdates?.size) {
- // The development server only allows a single locale but issue a warning if used programmatically (experimental)
- // with multiple locales and template HMR.
- if (i18nOptions.inlineLocales.size > 1) {
- inlineResult.warnings.push(
- `Component HMR updates can only be inlined with a single locale. The first locale will be used.`,
- );
+ // Update the result with all localized files.
+ executionResult.outputFiles = [
+ // Root and SSR entry files are not modified.
+ ...unModifiedOutputFiles,
+ // Updated files for each locale.
+ ...updatedOutputFiles,
+ ];
+
+ // Assets are only changed if not using the flat output option
+ if (!i18nOptions.flatOutput) {
+ executionResult.assetFiles = updatedAssetFiles;
}
- const firstLocale = [...i18nOptions.inlineLocales][0];
-
- for (const [id, content] of executionResult.templateUpdates) {
- const templateUpdateResult = await inliner.inlineTemplateUpdate(
- firstLocale,
- i18nOptions.locales[firstLocale].translation,
- content,
- id,
- );
- executionResult.templateUpdates.set(id, templateUpdateResult.code);
- inlineResult.errors.push(...templateUpdateResult.errors);
- inlineResult.warnings.push(...templateUpdateResult.warnings);
+
+ // Inline any template updates if present
+ if (executionResult.templateUpdates?.size) {
+ // The development server only allows a single locale but issue a warning if used programmatically (experimental)
+ // with multiple locales and template HMR.
+ if (i18nOptions.inlineLocales.size > 1) {
+ inlineResult.warnings.push(
+ `Component HMR updates can only be inlined with a single locale. The first locale will be used.`,
+ );
+ }
+ const firstLocale = [...i18nOptions.inlineLocales][0];
+
+ for (const [id, content] of executionResult.templateUpdates) {
+ const templateUpdateResult = await inliner.inlineTemplateUpdate(
+ firstLocale,
+ i18nOptions.locales[firstLocale].translation,
+ content,
+ id,
+ );
+ executionResult.templateUpdates.set(id, templateUpdateResult.code);
+ inlineResult.errors.push(...templateUpdateResult.errors);
+ inlineResult.warnings.push(...templateUpdateResult.warnings);
+ }
}
- }
- return inlineResult;
+ return inlineResult;
+ } finally {
+ await inliner.close();
+ }
}
/**
diff --git a/packages/angular/build/src/builders/application/index.ts b/packages/angular/build/src/builders/application/index.ts
index b83e3b48f270..d4671decc145 100644
--- a/packages/angular/build/src/builders/application/index.ts
+++ b/packages/angular/build/src/builders/application/index.ts
@@ -28,8 +28,6 @@ import {
import { Result, ResultKind } from './results';
import { Schema as ApplicationBuilderOptions } from './schema';
-const isNodeV22orHigher = Number(process.versions.node.split('.', 1)[0]) >= 22;
-
export type { ApplicationBuilderOptions };
export async function* buildApplicationInternal(
@@ -222,17 +220,10 @@ export async function* buildApplication(
await fs.writeFile(fullFilePath, file.contents);
} else {
// Copy file contents
- if (isNodeV22orHigher) {
- // Use newer `cp` API on Node.js 22+ (minimum v22 for CLI is 22.11)
- await fs.cp(file.inputPath, fullFilePath, {
- mode: fs.constants.COPYFILE_FICLONE,
- preserveTimestamps: true,
- });
- } else {
- // For Node.js 20 use `copyFile` (`cp` is not stable for v20)
- // TODO: Remove when Node.js 20 is no longer supported
- await fs.copyFile(file.inputPath, fullFilePath, fs.constants.COPYFILE_FICLONE);
- }
+ await fs.cp(file.inputPath, fullFilePath, {
+ mode: fs.constants.COPYFILE_FICLONE,
+ preserveTimestamps: true,
+ });
}
});
diff --git a/packages/angular/build/src/builders/application/options.ts b/packages/angular/build/src/builders/application/options.ts
index 83b7ea428f35..99d5d67efbfd 100644
--- a/packages/angular/build/src/builders/application/options.ts
+++ b/packages/angular/build/src/builders/application/options.ts
@@ -28,11 +28,11 @@ import { getProjectRootPaths, normalizeDirectoryPath } from '../../utils/project
import { addTrailingSlash, joinUrlParts, stripLeadingSlash } from '../../utils/url';
import {
Schema as ApplicationBuilderOptions,
- ExperimentalPlatform,
I18NTranslation,
OutputHashing,
OutputMode,
OutputPathClass,
+ Platform,
} from './schema';
/**
@@ -292,11 +292,11 @@ export async function normalizeOptions(
if (options.ssr === true) {
ssrOptions = {};
} else if (typeof options.ssr === 'object') {
- const { entry, experimentalPlatform = ExperimentalPlatform.Node } = options.ssr;
+ const { entry, platform = Platform.Node } = options.ssr;
ssrOptions = {
entry: entry && path.join(workspaceRoot, entry),
- platform: experimentalPlatform,
+ platform,
};
}
@@ -400,8 +400,9 @@ export async function normalizeOptions(
}
}
- const autoCsp = options.security?.autoCsp;
+ const { autoCsp, allowedHosts = [] } = options.security ?? {};
const security = {
+ allowedHosts,
autoCsp: autoCsp
? {
unsafeEval: autoCsp === true ? false : !!autoCsp.unsafeEval,
diff --git a/packages/angular/build/src/builders/application/schema.json b/packages/angular/build/src/builders/application/schema.json
index 8db4e6145b3f..8bf30aec8298 100644
--- a/packages/angular/build/src/builders/application/schema.json
+++ b/packages/angular/build/src/builders/application/schema.json
@@ -52,6 +52,14 @@
"type": "object",
"additionalProperties": false,
"properties": {
+ "allowedHosts": {
+ "description": "A list of hostnames that are allowed to access the server-side application. For more information, see https://angular.dev/best-practices/security#preventing-server-side-request-forgery-ssrf.",
+ "type": "array",
+ "uniqueItems": true,
+ "items": {
+ "type": "string"
+ }
+ },
"autoCsp": {
"description": "Enables automatic generation of a hash-based Strict Content Security Policy (https://web.dev/articles/strict-csp#choose-hash) based on scripts in index.html. Will default to true once we are out of experimental/preview phases.",
"default": false,
@@ -595,8 +603,8 @@
"type": "string",
"description": "The server entry-point that when executed will spawn the web server."
},
- "experimentalPlatform": {
- "description": "Specifies the platform for which the server bundle is generated. This affects the APIs and modules available in the server-side code. \n\n- `node`: (Default) Generates a bundle optimized for Node.js environments. \n- `neutral`: Generates a platform-neutral bundle suitable for environments like edge workers, and other serverless platforms. This option avoids using Node.js-specific APIs, making the bundle more portable. \n\nPlease note that this feature does not provide polyfills for Node.js modules. Additionally, it is experimental, and the schematics may undergo changes in future versions.",
+ "platform": {
+ "description": "Specifies the platform for which the server bundle is generated. This affects the APIs and modules available in the server-side code. \n\n- `node`: (Default) Generates a bundle optimized for Node.js environments. \n- `neutral`: Generates a platform-neutral bundle suitable for environments like edge workers, and other serverless platforms. This option avoids using Node.js-specific APIs, making the bundle more portable. \n\nPlease note that this feature does not provide polyfills for Node.js modules.",
"default": "node",
"enum": ["node", "neutral"]
}
diff --git a/packages/angular/build/src/builders/application/tests/behavior/component-stylesheets_spec.ts b/packages/angular/build/src/builders/application/tests/behavior/component-stylesheets_spec.ts
index ddae750a64a4..0fb1fb22c9fa 100644
--- a/packages/angular/build/src/builders/application/tests/behavior/component-stylesheets_spec.ts
+++ b/packages/angular/build/src/builders/application/tests/behavior/component-stylesheets_spec.ts
@@ -64,6 +64,25 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
);
});
+ it('should generate an error when a styleUrl points to a TypeScript file', async () => {
+ await harness.modifyFile('src/app/app.component.ts', (content) => {
+ return content.replace('./app.component.css', './app.component.ts');
+ });
+
+ harness.useTarget('build', {
+ ...BASE_OPTIONS,
+ });
+
+ const { result, logs } = await harness.executeOnce({ outputLogsOnFailure: false });
+ expect(result?.success).toBeFalse();
+ expect(logs).toContain(
+ jasmine.objectContaining({
+ level: 'error',
+ message: jasmine.stringContaining(`Could not find stylesheet file './app.component.ts'`),
+ }),
+ );
+ });
+
it('should generate an error for a missing stylesheet with JIT', async () => {
await harness.modifyFile('src/app/app.component.ts', (content) => {
return content.replace('./app.component.css', './not-present.css');
diff --git a/packages/angular/build/src/builders/application/tests/behavior/rebuild-component_styles_spec.ts b/packages/angular/build/src/builders/application/tests/behavior/rebuild-component_styles_spec.ts
index 26ae35a8221f..08b683439684 100644
--- a/packages/angular/build/src/builders/application/tests/behavior/rebuild-component_styles_spec.ts
+++ b/packages/angular/build/src/builders/application/tests/behavior/rebuild-component_styles_spec.ts
@@ -58,5 +58,85 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
]);
});
}
+
+ it('rebuilds component after error on rebuild from transitive import', async () => {
+ harness.useTarget('build', {
+ ...BASE_OPTIONS,
+ watch: true,
+ });
+
+ await harness.modifyFile('src/app/app.component.ts', (content) =>
+ content.replace('app.component.css', 'app.component.scss'),
+ );
+ await harness.writeFile('src/app/app.component.scss', "@import './a';");
+ await harness.writeFile('src/app/a.scss', '$primary: aqua;\\nh1 { color: $primary; }');
+
+ await harness.executeWithCases([
+ async ({ result }) => {
+ expect(result?.success).toBe(true);
+
+ harness.expectFile('dist/browser/main.js').content.toContain('color: aqua');
+
+ // Introduce a syntax error
+ await harness.writeFile(
+ 'src/app/a.scss',
+ 'invalid-invalid-invalid\\nh1 { color: $primary; }',
+ );
+ },
+ async ({ result }) => {
+ expect(result?.success).toBe(false);
+
+ // Fix the syntax error
+ await harness.writeFile('src/app/a.scss', '$primary: blue;\\nh1 { color: $primary; }');
+ },
+ ({ result }) => {
+ expect(result?.success).toBe(true);
+
+ harness.expectFile('dist/browser/main.js').content.toContain('color: blue');
+ },
+ ]);
+ });
+
+ it('rebuilds component after error on rebuild from deep transitive import with partials', async () => {
+ harness.useTarget('build', {
+ ...BASE_OPTIONS,
+ watch: true,
+ });
+
+ await harness.modifyFile('src/app/app.component.ts', (content) =>
+ content.replace('app.component.css', 'app.component.scss'),
+ );
+ await harness.writeFile('src/app/app.component.scss', "@import './intermediary';");
+ await harness.writeFile('src/app/_intermediary.scss', "@import './partial';");
+ await harness.writeFile('src/app/_partial.scss', '$primary: aqua;\\nh1 { color: $primary; }');
+
+ await harness.executeWithCases([
+ async ({ result }) => {
+ expect(result?.success).toBe(true);
+
+ harness.expectFile('dist/browser/main.js').content.toContain('color: aqua');
+
+ // Introduce a syntax error deeply
+ await harness.writeFile(
+ 'src/app/_partial.scss',
+ 'invalid-invalid-invalid\\nh1 { color: $primary; }',
+ );
+ },
+ async ({ result }) => {
+ expect(result?.success).toBe(false);
+
+ // Fix the syntax error deeply
+ await harness.writeFile(
+ 'src/app/_partial.scss',
+ '$primary: blue;\\nh1 { color: $primary; }',
+ );
+ },
+ ({ result }) => {
+ expect(result?.success).toBe(true);
+
+ harness.expectFile('dist/browser/main.js').content.toContain('color: blue');
+ },
+ ]);
+ });
});
});
diff --git a/packages/angular/build/src/builders/application/tests/behavior/rebuild-errors_spec.ts b/packages/angular/build/src/builders/application/tests/behavior/rebuild-errors_spec.ts
index fa384be88080..de53c7223fff 100644
--- a/packages/angular/build/src/builders/application/tests/behavior/rebuild-errors_spec.ts
+++ b/packages/angular/build/src/builders/application/tests/behavior/rebuild-errors_spec.ts
@@ -33,7 +33,7 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
import { Directive, Input } from '@angular/core';
@Directive({ selector: 'dir', standalone: false })
export class Dir {
- @Input() foo: number;
+ @Input() foo!: number;
}
`;
diff --git a/packages/angular/build/src/builders/application/tests/behavior/typescript-isolated-modules_spec.ts b/packages/angular/build/src/builders/application/tests/behavior/typescript-isolated-modules_spec.ts
index 06e66cbd6da9..bef97e545ad9 100644
--- a/packages/angular/build/src/builders/application/tests/behavior/typescript-isolated-modules_spec.ts
+++ b/packages/angular/build/src/builders/application/tests/behavior/typescript-isolated-modules_spec.ts
@@ -55,6 +55,9 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
tsconfig.compilerOptions.isolatedModules = true;
tsconfig.compilerOptions.jsx = 'react-jsx';
+ // There's an implicit `any`, because we don't include the React typings.
+ tsconfig.compilerOptions.noImplicitAny = false;
+
return JSON.stringify(tsconfig);
});
diff --git a/packages/angular/build/src/builders/application/tests/behavior/typescript-path-mapping_spec.ts b/packages/angular/build/src/builders/application/tests/behavior/typescript-path-mapping_spec.ts
index 41539df239f2..416ee9c227bd 100644
--- a/packages/angular/build/src/builders/application/tests/behavior/typescript-path-mapping_spec.ts
+++ b/packages/angular/build/src/builders/application/tests/behavior/typescript-path-mapping_spec.ts
@@ -81,8 +81,9 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
await harness.modifyFile('tsconfig.json', (content) => {
const tsconfig = JSON.parse(content);
tsconfig.compilerOptions.paths = {
- 'app-module': ['a.js'],
+ 'app-module': ['./a.js'],
};
+ tsconfig.compilerOptions.allowJs = true;
return JSON.stringify(tsconfig);
});
diff --git a/packages/angular/build/src/builders/application/tests/behavior/typescript-resolve-json_spec.ts b/packages/angular/build/src/builders/application/tests/behavior/typescript-resolve-json_spec.ts
index cf21d5545f7a..6ad5bbb3a9de 100644
--- a/packages/angular/build/src/builders/application/tests/behavior/typescript-resolve-json_spec.ts
+++ b/packages/angular/build/src/builders/application/tests/behavior/typescript-resolve-json_spec.ts
@@ -20,7 +20,8 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
// Enable tsconfig resolveJsonModule option in tsconfig
await harness.modifyFile('tsconfig.json', (content) => {
const tsconfig = JSON.parse(content);
- tsconfig.compilerOptions.moduleResolution = 'node';
+ tsconfig.compilerOptions.moduleResolution = 'node16';
+ tsconfig.compilerOptions.module = 'node16';
tsconfig.compilerOptions.resolveJsonModule = true;
return JSON.stringify(tsconfig);
@@ -44,7 +45,8 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
// Enable tsconfig resolveJsonModule option in tsconfig
await harness.modifyFile('tsconfig.json', (content) => {
const tsconfig = JSON.parse(content);
- tsconfig.compilerOptions.moduleResolution = 'node';
+ tsconfig.compilerOptions.moduleResolution = 'node16';
+ tsconfig.compilerOptions.module = 'node16';
tsconfig.compilerOptions.resolveJsonModule = undefined;
return JSON.stringify(tsconfig);
@@ -73,7 +75,8 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
// Enable tsconfig resolveJsonModule option in tsconfig
await harness.modifyFile('tsconfig.json', (content) => {
const tsconfig = JSON.parse(content);
- tsconfig.compilerOptions.moduleResolution = 'node';
+ tsconfig.compilerOptions.moduleResolution = 'node16';
+ tsconfig.compilerOptions.module = 'node16';
tsconfig.compilerOptions.resolveJsonModule = false;
return JSON.stringify(tsconfig);
diff --git a/packages/angular/build/src/builders/application/tests/options/allowed-common-js-dependencies_spec.ts b/packages/angular/build/src/builders/application/tests/options/allowed-common-js-dependencies_spec.ts
index bcc361ccdbe1..7521b1a1a495 100644
--- a/packages/angular/build/src/builders/application/tests/options/allowed-common-js-dependencies_spec.ts
+++ b/packages/angular/build/src/builders/application/tests/options/allowed-common-js-dependencies_spec.ts
@@ -129,17 +129,13 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
it('should not show warning in JIT for templateUrl and styleUrl when using paths', async () => {
await harness.modifyFile('tsconfig.json', (content) => {
- return content.replace(
- /"baseUrl": ".\/",/,
- `
- "baseUrl": "./",
- "paths": {
- "@app/*": [
- "src/app/*"
- ]
- },
- `,
- );
+ const tsconfig = JSON.parse(content);
+ tsconfig.compilerOptions ??= {};
+ tsconfig.compilerOptions.paths = {
+ '@app/*': ['./src/app/*'],
+ };
+
+ return JSON.stringify(tsconfig, null, 2);
});
await harness.modifyFile('src/app/app.module.ts', (content) =>
diff --git a/packages/angular/build/src/builders/application/tests/options/polyfills_spec.ts b/packages/angular/build/src/builders/application/tests/options/polyfills_spec.ts
index 8b5cc3a09ab3..5fff9bd33a7c 100644
--- a/packages/angular/build/src/builders/application/tests/options/polyfills_spec.ts
+++ b/packages/angular/build/src/builders/application/tests/options/polyfills_spec.ts
@@ -26,6 +26,9 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
const tsconfig = JSON.parse(content);
tsconfig.compilerOptions.baseUrl = baseUrl;
+ // The test is specifically testing `baseUrl` which is deprecated in TS6.
+ tsconfig.compilerOptions.ignoreDeprecations = '6.0';
+
return JSON.stringify(tsconfig);
});
});
diff --git a/packages/angular/build/src/builders/dev-server/options.ts b/packages/angular/build/src/builders/dev-server/options.ts
index b6da278f2936..5473da832449 100644
--- a/packages/angular/build/src/builders/dev-server/options.ts
+++ b/packages/angular/build/src/builders/dev-server/options.ts
@@ -86,10 +86,20 @@ export async function normalizeOptions(
}
}
+ let port = options.port ?? 4200;
+ // Overwrite port, if process.env.PORT is available.
+ if (process.env.PORT) {
+ const envPort = Number(process.env.PORT);
+
+ if (!isNaN(envPort)) {
+ port = envPort;
+ logger.info(`Environment variable "PORT" detected. Using port ${envPort}.`);
+ }
+ }
+
// Initial options to keep
const {
host,
- port,
poll,
open,
verbose,
@@ -111,7 +121,7 @@ export async function normalizeOptions(
return {
buildTarget,
host: host ?? 'localhost',
- port: port ?? 4200,
+ port,
poll,
open,
verbose,
diff --git a/packages/angular/build/src/builders/dev-server/tests/behavior/build-assets_spec.ts b/packages/angular/build/src/builders/dev-server/tests/behavior/build-assets_spec.ts
index f7c7a0acb33a..7d3e1ffc414b 100644
--- a/packages/angular/build/src/builders/dev-server/tests/behavior/build-assets_spec.ts
+++ b/packages/angular/build/src/builders/dev-server/tests/behavior/build-assets_spec.ts
@@ -21,6 +21,26 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT
"import {foo} from 'unresolved'; /* a comment */const foo = `bar`;\n\n\n";
describe('Behavior: "browser builder assets"', () => {
+ it('serves a project extensionless asset unmodified', async () => {
+ await harness.writeFile('src/extensionless', javascriptFileContent);
+
+ setupTarget(harness, {
+ assets: ['src/extensionless'],
+ optimization: {
+ scripts: true,
+ },
+ });
+
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ });
+
+ const { result, response } = await executeOnceAndFetch(harness, 'extensionless');
+
+ expect(result?.success).toBeTrue();
+ expect(await response?.text()).toContain(javascriptFileContent);
+ });
+
it('serves a project JavaScript asset unmodified', async () => {
await harness.writeFile('src/extra.js', javascriptFileContent);
diff --git a/packages/angular/build/src/builders/dev-server/tests/behavior/build-budgets_spec.ts b/packages/angular/build/src/builders/dev-server/tests/behavior/build-budgets_spec.ts
index aee551e78b48..97a68506fcf5 100644
--- a/packages/angular/build/src/builders/dev-server/tests/behavior/build-budgets_spec.ts
+++ b/packages/angular/build/src/builders/dev-server/tests/behavior/build-budgets_spec.ts
@@ -11,29 +11,25 @@ import { executeDevServer } from '../../index';
import { describeServeBuilder } from '../jasmine-helpers';
import { BASE_OPTIONS, DEV_SERVER_BUILDER_INFO } from '../setup';
-describeServeBuilder(
- executeDevServer,
- DEV_SERVER_BUILDER_INFO,
- (harness, setupTarget, isViteRun) => {
- // TODO(fix-vite): currently this is broken in vite.
- (isViteRun ? xdescribe : describe)('Behavior: "browser builder budgets"', () => {
- beforeEach(() => {
- setupTarget(harness, {
- // Add a budget error for any file over 100 bytes
- budgets: [{ type: BudgetType.All, maximumError: '100b' }],
- optimization: true,
- });
+describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupTarget) => {
+ // TODO(fix-vite): currently this is broken in vite.
+ xdescribe('Behavior: "browser builder budgets"', () => {
+ beforeEach(() => {
+ setupTarget(harness, {
+ // Add a budget error for any file over 100 bytes
+ budgets: [{ type: BudgetType.All, maximumError: '100b' }],
+ optimization: true,
});
+ });
- it('should ignore budgets defined in the "buildTarget" options', async () => {
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- });
+ it('should ignore budgets defined in the "buildTarget" options', async () => {
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ });
- const { result } = await harness.executeOnce();
+ const { result } = await harness.executeOnce();
- expect(result?.success).toBe(true);
- });
+ expect(result?.success).toBe(true);
});
- },
-);
+ });
+});
diff --git a/packages/angular/build/src/builders/dev-server/tests/behavior/build-conditions_spec.ts b/packages/angular/build/src/builders/dev-server/tests/behavior/build-conditions_spec.ts
index 2a7d59d8d574..aef1973d4a48 100644
--- a/packages/angular/build/src/builders/dev-server/tests/behavior/build-conditions_spec.ts
+++ b/packages/angular/build/src/builders/dev-server/tests/behavior/build-conditions_spec.ts
@@ -15,86 +15,74 @@ import { executeOnceAndFetch } from '../execute-fetch';
import { describeServeBuilder } from '../jasmine-helpers';
import { BASE_OPTIONS, DEV_SERVER_BUILDER_INFO } from '../setup';
-describeServeBuilder(
- executeDevServer,
- DEV_SERVER_BUILDER_INFO,
- (harness, setupTarget, isApplicationBuilder) => {
- describe('Behavior: "conditional imports"', () => {
- if (!isApplicationBuilder) {
- it('requires esbuild', () => {
- expect(true).toBeTrue();
- });
-
- return;
- }
-
- beforeEach(async () => {
- setupTarget(harness);
+describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupTarget) => {
+ describe('Behavior: "conditional imports"', () => {
+ beforeEach(async () => {
+ setupTarget(harness);
- await setupConditionImport(harness);
- });
+ await setupConditionImport(harness);
+ });
- interface ImportsTestCase {
- name: string;
- mapping: unknown;
- output?: string;
- }
+ interface ImportsTestCase {
+ name: string;
+ mapping: unknown;
+ output?: string;
+ }
- const GOOD_TARGET = './src/good.js';
- const BAD_TARGET = './src/bad.js';
+ const GOOD_TARGET = './src/good.js';
+ const BAD_TARGET = './src/bad.js';
- const testCases: ImportsTestCase[] = [
- { name: 'simple string', mapping: GOOD_TARGET },
- {
- name: 'default fallback without matching condition',
- mapping: {
- 'never': BAD_TARGET,
- 'default': GOOD_TARGET,
- },
+ const testCases: ImportsTestCase[] = [
+ { name: 'simple string', mapping: GOOD_TARGET },
+ {
+ name: 'default fallback without matching condition',
+ mapping: {
+ 'never': BAD_TARGET,
+ 'default': GOOD_TARGET,
},
- {
- name: 'development condition',
- mapping: {
- 'development': GOOD_TARGET,
- 'default': BAD_TARGET,
- },
+ },
+ {
+ name: 'development condition',
+ mapping: {
+ 'development': GOOD_TARGET,
+ 'default': BAD_TARGET,
},
- {
- name: 'production condition',
- mapping: {
- 'production': BAD_TARGET,
- 'default': GOOD_TARGET,
- },
+ },
+ {
+ name: 'production condition',
+ mapping: {
+ 'production': BAD_TARGET,
+ 'default': GOOD_TARGET,
},
- {
- name: 'browser condition (in browser)',
- mapping: {
- 'browser': GOOD_TARGET,
- 'default': BAD_TARGET,
- },
+ },
+ {
+ name: 'browser condition (in browser)',
+ mapping: {
+ 'browser': GOOD_TARGET,
+ 'default': BAD_TARGET,
},
- ];
+ },
+ ];
- for (const testCase of testCases) {
- describe(testCase.name, () => {
- beforeEach(async () => {
- await setTargetMapping(harness, testCase.mapping);
- });
+ for (const testCase of testCases) {
+ describe(testCase.name, () => {
+ beforeEach(async () => {
+ await setTargetMapping(harness, testCase.mapping);
+ });
- it('resolves to expected target', async () => {
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- });
+ it('resolves to expected target', async () => {
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ });
- const { result, response } = await executeOnceAndFetch(harness, '/main.js');
+ const { result, response } = await executeOnceAndFetch(harness, '/main.js');
- expect(result?.success).toBeTrue();
- const output = await response?.text();
- expect(output).toContain('good-value');
- expect(output).not.toContain('bad-value');
- });
+ expect(result?.success).toBeTrue();
+ const output = await response?.text();
+ expect(output).toContain('good-value');
+ expect(output).not.toContain('bad-value');
});
- }
- });
- },
-);
+ });
+ }
+ });
+});
diff --git a/packages/angular/build/src/builders/dev-server/tests/behavior/build_translation_watch_spec.ts b/packages/angular/build/src/builders/dev-server/tests/behavior/build_translation_watch_spec.ts
index b7d65e52e966..24dca8a6a5dc 100644
--- a/packages/angular/build/src/builders/dev-server/tests/behavior/build_translation_watch_spec.ts
+++ b/packages/angular/build/src/builders/dev-server/tests/behavior/build_translation_watch_spec.ts
@@ -12,70 +12,66 @@ import { executeDevServer } from '../../index';
import { describeServeBuilder } from '../jasmine-helpers';
import { BASE_OPTIONS, DEV_SERVER_BUILDER_INFO } from '../setup';
-describeServeBuilder(
- executeDevServer,
- DEV_SERVER_BUILDER_INFO,
- (harness, setupTarget, isViteRun) => {
- // TODO(fix-vite): currently this is broken in vite.
- (isViteRun ? xdescribe : describe)('Behavior: "i18n translation file watching"', () => {
- beforeEach(() => {
- harness.useProject('test', {
- root: '.',
- sourceRoot: 'src',
- cli: {
- cache: {
- enabled: false,
- },
+describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupTarget) => {
+ // TODO(fix-vite): currently this is broken in vite.
+ xdescribe('Behavior: "i18n translation file watching"', () => {
+ beforeEach(() => {
+ harness.useProject('test', {
+ root: '.',
+ sourceRoot: 'src',
+ cli: {
+ cache: {
+ enabled: false,
},
- i18n: {
- locales: {
- fr: 'src/locales/messages.fr.xlf',
- },
+ },
+ i18n: {
+ locales: {
+ fr: 'src/locales/messages.fr.xlf',
},
- });
-
- setupTarget(harness, { localize: ['fr'] });
+ },
});
- it('watches i18n translation files by default', async () => {
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- watch: true,
- });
+ setupTarget(harness, { localize: ['fr'] });
+ });
+
+ it('watches i18n translation files by default', async () => {
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ watch: true,
+ });
- await harness.writeFile(
- 'src/app/app.component.html',
- `
+ await harness.writeFile(
+ 'src/app/app.component.html',
+ `
Hello {{ title }}!
`,
- );
+ );
- await harness.writeFile('src/locales/messages.fr.xlf', TRANSLATION_FILE_CONTENT);
+ await harness.writeFile('src/locales/messages.fr.xlf', TRANSLATION_FILE_CONTENT);
- await harness.executeWithCases([
- async ({ result }) => {
- expect(result?.success).toBe(true);
+ await harness.executeWithCases([
+ async ({ result }) => {
+ expect(result?.success).toBe(true);
- const mainUrl = new URL('main.js', `${result?.baseUrl}`);
- const response = await fetch(mainUrl);
- expect(await response?.text()).toContain('Bonjour');
+ const mainUrl = new URL('main.js', `${result?.baseUrl}`);
+ const response = await fetch(mainUrl);
+ expect(await response?.text()).toContain('Bonjour');
- await harness.modifyFile('src/locales/messages.fr.xlf', (content) =>
- content.replace('Bonjour', 'Salut'),
- );
- },
- async ({ result }) => {
- expect(result?.success).toBe(true);
+ await harness.modifyFile('src/locales/messages.fr.xlf', (content) =>
+ content.replace('Bonjour', 'Salut'),
+ );
+ },
+ async ({ result }) => {
+ expect(result?.success).toBe(true);
- const mainUrl = new URL('main.js', `${result?.baseUrl}`);
- const response = await fetch(mainUrl);
- expect(await response?.text()).toContain('Salut');
- },
- ]);
- });
+ const mainUrl = new URL('main.js', `${result?.baseUrl}`);
+ const response = await fetch(mainUrl);
+ expect(await response?.text()).toContain('Salut');
+ },
+ ]);
});
- },
-);
+ });
+});
const TRANSLATION_FILE_CONTENT = `
diff --git a/packages/angular/build/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts b/packages/angular/build/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts
index efdd749de258..3b7fa033aed6 100644
--- a/packages/angular/build/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts
+++ b/packages/angular/build/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts
@@ -133,145 +133,138 @@ async function goToPageAndWaitForWS(page: Page, url: string): Promise {
await client.detach();
}
-describeServeBuilder(
- executeDevServer,
- DEV_SERVER_BUILDER_INFO,
- (harness, setupTarget, isViteRun) => {
- // TODO(fix-vite): currently this is broken in vite.
- (isViteRun ? xdescribe : describe)(
- 'Behavior: "Dev-server builder live-reload with proxies"',
- () => {
- let browser: Browser;
- let page: Page;
+describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupTarget) => {
+ // TODO(fix-vite): currently this is broken in vite.
+ xdescribe('Behavior: "Dev-server builder live-reload with proxies"', () => {
+ let browser: Browser;
+ let page: Page;
- const SERVE_OPTIONS = Object.freeze({
- ...BASE_OPTIONS,
- hmr: false,
- watch: true,
- liveReload: true,
- });
+ const SERVE_OPTIONS = Object.freeze({
+ ...BASE_OPTIONS,
+ hmr: false,
+ watch: true,
+ liveReload: true,
+ });
- beforeAll(async () => {
- browser = await puppeteer.launch({
- // MacOSX users need to set the local binary manually because Chrome has lib files with
- // spaces in them which Bazel does not support in runfiles
- // See: https://github.com/angular/angular-cli/pull/17624
- // eslint-disable-next-line max-len
- // executablePath: '/Users//git/angular-cli/node_modules/puppeteer/.local-chromium/mac-818858/chrome-mac/Chromium.app/Contents/MacOS/Chromium',
- ignoreHTTPSErrors: true,
- args: ['--no-sandbox', '--disable-gpu'],
- });
- });
+ beforeAll(async () => {
+ browser = await puppeteer.launch({
+ // MacOSX users need to set the local binary manually because Chrome has lib files with
+ // spaces in them which Bazel does not support in runfiles
+ // See: https://github.com/angular/angular-cli/pull/17624
+ // eslint-disable-next-line max-len
+ // executablePath: '/Users//git/angular-cli/node_modules/puppeteer/.local-chromium/mac-818858/chrome-mac/Chromium.app/Contents/MacOS/Chromium',
+ acceptInsecureCerts: true,
+ args: ['--no-sandbox', '--disable-gpu'],
+ });
+ });
- afterAll(async () => {
- await browser.close();
- });
+ afterAll(async () => {
+ await browser.close();
+ });
- beforeEach(async () => {
- setupTarget(harness, {
- polyfills: ['src/polyfills.ts'],
- });
+ beforeEach(async () => {
+ setupTarget(harness, {
+ polyfills: ['src/polyfills.ts'],
+ });
- page = await browser.newPage();
- });
+ page = await browser.newPage();
+ });
- afterEach(async () => {
- await page.close();
- });
+ afterEach(async () => {
+ await page.close();
+ });
- it('works without proxy', async () => {
- harness.useTarget('serve', {
- ...SERVE_OPTIONS,
- });
+ it('works without proxy', async () => {
+ harness.useTarget('serve', {
+ ...SERVE_OPTIONS,
+ });
- await harness.writeFile('src/app/app.component.html', '{{ title }}
');
+ await harness.writeFile('src/app/app.component.html', '{{ title }}
');
- await harness.executeWithCases([
- async ({ result }) => {
- expect(result?.success).toBeTrue();
- if (typeof result?.baseUrl !== 'string') {
- throw new Error('Expected "baseUrl" to be a string.');
- }
+ await harness.executeWithCases([
+ async ({ result }) => {
+ expect(result?.success).toBeTrue();
+ if (typeof result?.baseUrl !== 'string') {
+ throw new Error('Expected "baseUrl" to be a string.');
+ }
- await goToPageAndWaitForWS(page, result.baseUrl);
- await harness.modifyFile('src/app/app.component.ts', (content) =>
- content.replace(`'app'`, `'app-live-reload'`),
- );
- },
- async ({ result }) => {
- expect(result?.success).toBeTrue();
- const innerText = await page.evaluate(() => document.querySelector('p').innerText);
- expect(innerText).toBe('app-live-reload');
- },
- ]);
- });
+ await goToPageAndWaitForWS(page, result.baseUrl);
+ await harness.modifyFile('src/app/app.component.ts', (content) =>
+ content.replace(`'app'`, `'app-live-reload'`),
+ );
+ },
+ async ({ result }) => {
+ expect(result?.success).toBeTrue();
+ const innerText = await page.evaluate(() => document.querySelector('p').innerText);
+ expect(innerText).toBe('app-live-reload');
+ },
+ ]);
+ });
- it('works without http -> http proxy', async () => {
- harness.useTarget('serve', {
- ...SERVE_OPTIONS,
- });
+ it('works without http -> http proxy', async () => {
+ harness.useTarget('serve', {
+ ...SERVE_OPTIONS,
+ });
- await harness.writeFile('src/app/app.component.html', '{{ title }}
');
+ await harness.writeFile('src/app/app.component.html', '{{ title }}
');
- let proxy: ProxyInstance | undefined;
- try {
- await harness.executeWithCases([
- async ({ result }) => {
- expect(result?.success).toBeTrue();
- if (typeof result?.baseUrl !== 'string') {
- throw new Error('Expected "baseUrl" to be a string.');
- }
+ let proxy: ProxyInstance | undefined;
+ try {
+ await harness.executeWithCases([
+ async ({ result }) => {
+ expect(result?.success).toBeTrue();
+ if (typeof result?.baseUrl !== 'string') {
+ throw new Error('Expected "baseUrl" to be a string.');
+ }
- proxy = await createProxy(result.baseUrl, false);
- await goToPageAndWaitForWS(page, proxy.url);
- await harness.modifyFile('src/app/app.component.ts', (content) =>
- content.replace(`'app'`, `'app-live-reload'`),
- );
- },
- async ({ result }) => {
- expect(result?.success).toBeTrue();
- const innerText = await page.evaluate(() => document.querySelector('p').innerText);
- expect(innerText).toBe('app-live-reload');
- },
- ]);
- } finally {
- proxy?.server.close();
- }
- });
+ proxy = await createProxy(result.baseUrl, false);
+ await goToPageAndWaitForWS(page, proxy.url);
+ await harness.modifyFile('src/app/app.component.ts', (content) =>
+ content.replace(`'app'`, `'app-live-reload'`),
+ );
+ },
+ async ({ result }) => {
+ expect(result?.success).toBeTrue();
+ const innerText = await page.evaluate(() => document.querySelector('p').innerText);
+ expect(innerText).toBe('app-live-reload');
+ },
+ ]);
+ } finally {
+ proxy?.server.close();
+ }
+ });
- it('works without https -> http proxy', async () => {
- harness.useTarget('serve', {
- ...SERVE_OPTIONS,
- });
+ it('works without https -> http proxy', async () => {
+ harness.useTarget('serve', {
+ ...SERVE_OPTIONS,
+ });
- await harness.writeFile('src/app/app.component.html', '{{ title }}
');
+ await harness.writeFile('src/app/app.component.html', '{{ title }}
');
- let proxy: ProxyInstance | undefined;
- try {
- await harness.executeWithCases([
- async ({ result }) => {
- expect(result?.success).toBeTrue();
- if (typeof result?.baseUrl !== 'string') {
- throw new Error('Expected "baseUrl" to be a string.');
- }
+ let proxy: ProxyInstance | undefined;
+ try {
+ await harness.executeWithCases([
+ async ({ result }) => {
+ expect(result?.success).toBeTrue();
+ if (typeof result?.baseUrl !== 'string') {
+ throw new Error('Expected "baseUrl" to be a string.');
+ }
- proxy = await createProxy(result.baseUrl, true);
- await goToPageAndWaitForWS(page, proxy.url);
- await harness.modifyFile('src/app/app.component.ts', (content) =>
- content.replace(`'app'`, `'app-live-reload'`),
- );
- },
- async ({ result }) => {
- expect(result?.success).toBeTrue();
- const innerText = await page.evaluate(() => document.querySelector('p').innerText);
- expect(innerText).toBe('app-live-reload');
- },
- ]);
- } finally {
- proxy?.server.close();
- }
- });
- },
- );
- },
-);
+ proxy = await createProxy(result.baseUrl, true);
+ await goToPageAndWaitForWS(page, proxy.url);
+ await harness.modifyFile('src/app/app.component.ts', (content) =>
+ content.replace(`'app'`, `'app-live-reload'`),
+ );
+ },
+ async ({ result }) => {
+ expect(result?.success).toBeTrue();
+ const innerText = await page.evaluate(() => document.querySelector('p').innerText);
+ expect(innerText).toBe('app-live-reload');
+ },
+ ]);
+ } finally {
+ proxy?.server.close();
+ }
+ });
+ });
+});
diff --git a/packages/angular/build/src/builders/dev-server/tests/behavior/serve_service-worker_spec.ts b/packages/angular/build/src/builders/dev-server/tests/behavior/serve_service-worker_spec.ts
index b3b63c3a3093..10e2cee70465 100644
--- a/packages/angular/build/src/builders/dev-server/tests/behavior/serve_service-worker_spec.ts
+++ b/packages/angular/build/src/builders/dev-server/tests/behavior/serve_service-worker_spec.ts
@@ -36,180 +36,174 @@ const manifest = {
],
};
-describeServeBuilder(
- executeDevServer,
- DEV_SERVER_BUILDER_INFO,
- (harness, setupTarget, isViteRun) => {
- describe('Behavior: "dev-server builder serves service worker"', () => {
- beforeEach(async () => {
- // Application code is not needed for these tests
- await harness.writeFile('src/main.ts', '');
- await harness.writeFile('src/polyfills.ts', '');
-
- harness.useProject('test', {
- root: '.',
- sourceRoot: 'src',
- cli: {
- cache: {
- enabled: false,
- },
+describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupTarget) => {
+ describe('Behavior: "dev-server builder serves service worker"', () => {
+ beforeEach(async () => {
+ // Application code is not needed for these tests
+ await harness.writeFile('src/main.ts', '');
+ await harness.writeFile('src/polyfills.ts', '');
+
+ harness.useProject('test', {
+ root: '.',
+ sourceRoot: 'src',
+ cli: {
+ cache: {
+ enabled: false,
},
- i18n: {
- sourceLocale: {
- code: 'fr',
- },
+ },
+ i18n: {
+ sourceLocale: {
+ code: 'fr',
},
- });
+ },
+ });
+ });
+
+ it('works with service worker', async () => {
+ setupTarget(harness, {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ serviceWorker: 'ngsw-config.json',
+ assets: ['src/favicon.ico', 'src/assets'],
+ styles: ['src/styles.css'],
});
- it('works with service worker', async () => {
- setupTarget(harness, {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- serviceWorker: (isViteRun ? 'ngsw-config.json' : true) as any,
- assets: ['src/favicon.ico', 'src/assets'],
- styles: ['src/styles.css'],
- });
-
- await harness.writeFiles({
- 'ngsw-config.json': JSON.stringify(manifest),
- 'src/assets/folder-asset.txt': 'folder-asset.txt',
- 'src/styles.css': `body { background: url(./spectrum.png); }`,
- });
-
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- });
-
- const { result, response } = await executeOnceAndFetch(harness, '/ngsw.json');
-
- expect(result?.success).toBeTrue();
-
- expect(await response?.json()).toEqual(
- jasmine.objectContaining({
- configVersion: 1,
- index: '/index.html',
- navigationUrls: [
- { positive: true, regex: '^\\/.*$' },
- { positive: false, regex: '^\\/(?:.+\\/)?[^/]*\\.[^/]*$' },
- { positive: false, regex: '^\\/(?:.+\\/)?[^/]*__[^/]*$' },
- { positive: false, regex: '^\\/(?:.+\\/)?[^/]*__[^/]*\\/.*$' },
- ],
- assetGroups: [
- {
- name: 'app',
- installMode: 'prefetch',
- updateMode: 'prefetch',
- urls: ['/favicon.ico', '/index.html'],
- cacheQueryOptions: {
- ignoreVary: true,
- },
- patterns: [],
+ await harness.writeFiles({
+ 'ngsw-config.json': JSON.stringify(manifest),
+ 'src/assets/folder-asset.txt': 'folder-asset.txt',
+ 'src/styles.css': `body { background: url(./spectrum.png); }`,
+ });
+
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ });
+
+ const { result, response } = await executeOnceAndFetch(harness, '/ngsw.json');
+
+ expect(result?.success).toBeTrue();
+
+ expect(await response?.json()).toEqual(
+ jasmine.objectContaining({
+ configVersion: 1,
+ index: '/index.html',
+ navigationUrls: [
+ { positive: true, regex: '^\\/.*$' },
+ { positive: false, regex: '^\\/(?:.+\\/)?[^/]*\\.[^/]*$' },
+ { positive: false, regex: '^\\/(?:.+\\/)?[^/]*__[^/]*$' },
+ { positive: false, regex: '^\\/(?:.+\\/)?[^/]*__[^/]*\\/.*$' },
+ ],
+ assetGroups: [
+ {
+ name: 'app',
+ installMode: 'prefetch',
+ updateMode: 'prefetch',
+ urls: ['/favicon.ico', '/index.html'],
+ cacheQueryOptions: {
+ ignoreVary: true,
},
- {
- name: 'assets',
- installMode: 'lazy',
- updateMode: 'prefetch',
- urls: ['/assets/folder-asset.txt', '/media/spectrum.png'],
- cacheQueryOptions: {
- ignoreVary: true,
- },
- patterns: [],
+ patterns: [],
+ },
+ {
+ name: 'assets',
+ installMode: 'lazy',
+ updateMode: 'prefetch',
+ urls: ['/assets/folder-asset.txt', '/media/spectrum.png'],
+ cacheQueryOptions: {
+ ignoreVary: true,
},
- ],
- dataGroups: [],
- hashTable: {
- '/favicon.ico': '84161b857f5c547e3699ddfbffc6d8d737542e01',
- '/assets/folder-asset.txt': '617f202968a6a81050aa617c2e28e1dca11ce8d4',
- '/index.html': isViteRun
- ? 'e5b73e6798d2782bf59dd5272d254d5bde364695'
- : '9d232e3e13b4605d197037224a2a6303dd337480',
- '/media/spectrum.png': '8d048ece46c0f3af4b598a95fd8e4709b631c3c0',
+ patterns: [],
},
- }),
- );
+ ],
+ dataGroups: [],
+ hashTable: {
+ '/favicon.ico': '84161b857f5c547e3699ddfbffc6d8d737542e01',
+ '/assets/folder-asset.txt': '617f202968a6a81050aa617c2e28e1dca11ce8d4',
+ '/index.html': 'e5b73e6798d2782bf59dd5272d254d5bde364695',
+ '/media/spectrum.png': '8d048ece46c0f3af4b598a95fd8e4709b631c3c0',
+ },
+ }),
+ );
+ });
+
+ it('works with localize', async () => {
+ setupTarget(harness, {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ serviceWorker: 'ngsw-config.json' as any,
+ assets: ['src/favicon.ico', 'src/assets'],
+ styles: ['src/styles.css'],
+ localize: ['fr'],
});
- it('works with localize', async () => {
- setupTarget(harness, {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- serviceWorker: (isViteRun ? 'ngsw-config.json' : true) as any,
- assets: ['src/favicon.ico', 'src/assets'],
- styles: ['src/styles.css'],
- localize: ['fr'],
- });
+ await harness.writeFiles({
+ 'ngsw-config.json': JSON.stringify(manifest),
+ 'src/assets/folder-asset.txt': 'folder-asset.txt',
+ 'src/styles.css': `body { background: url(./spectrum.png); }`,
+ });
+
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ });
- await harness.writeFiles({
- 'ngsw-config.json': JSON.stringify(manifest),
- 'src/assets/folder-asset.txt': 'folder-asset.txt',
- 'src/styles.css': `body { background: url(./spectrum.png); }`,
- });
+ const { result, response } = await executeOnceAndFetch(harness, '/ngsw.json');
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- });
+ expect(result?.success).toBeTrue();
- const { result, response } = await executeOnceAndFetch(harness, '/ngsw.json');
+ expect(await response?.json()).toBeDefined();
+ });
- expect(result?.success).toBeTrue();
+ // TODO(fix-vite): currently this is broken in vite due to watcher never terminates.
+ xit('works in watch mode', async () => {
+ setupTarget(harness, {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ serviceWorker: 'ngsw-config.json' as any,
+ assets: ['src/favicon.ico', 'src/assets'],
+ styles: ['src/styles.css'],
+ });
- expect(await response?.json()).toBeDefined();
+ await harness.writeFiles({
+ 'ngsw-config.json': JSON.stringify(manifest),
+ 'src/assets/folder-asset.txt': 'folder-asset.txt',
+ 'src/styles.css': `body { background: url(./spectrum.png); }`,
});
- // TODO(fix-vite): currently this is broken in vite due to watcher never terminates.
- (isViteRun ? xit : it)('works in watch mode', async () => {
- setupTarget(harness, {
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- serviceWorker: (isViteRun ? 'ngsw-config.json' : true) as any,
- assets: ['src/favicon.ico', 'src/assets'],
- styles: ['src/styles.css'],
- });
-
- await harness.writeFiles({
- 'ngsw-config.json': JSON.stringify(manifest),
- 'src/assets/folder-asset.txt': 'folder-asset.txt',
- 'src/styles.css': `body { background: url(./spectrum.png); }`,
- });
-
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- watch: true,
- });
-
- await harness.executeWithCases([
- async ({ result }) => {
- expect(result?.success).toBeTrue();
- const response = await fetch(new URL('ngsw.json', `${result?.baseUrl}`));
- const { hashTable } = (await response.json()) as { hashTable: object };
- const hashTableEntries = Object.keys(hashTable);
-
- expect(hashTableEntries).toEqual([
- '/assets/folder-asset.txt',
- '/favicon.ico',
- '/index.html',
- '/media/spectrum.png',
- ]);
-
- await harness.writeFile(
- 'src/assets/folder-new-asset.txt',
- harness.readFile('src/assets/folder-asset.txt'),
- );
- },
- async ({ result }) => {
- expect(result?.success).toBeTrue();
- const response = await fetch(new URL('ngsw.json', `${result?.baseUrl}`));
- const { hashTable } = (await response.json()) as { hashTable: object };
- const hashTableEntries = Object.keys(hashTable);
-
- expect(hashTableEntries).toEqual([
- '/assets/folder-asset.txt',
- '/assets/folder-new-asset.txt',
- '/favicon.ico',
- '/index.html',
- '/media/spectrum.png',
- ]);
- },
- ]);
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ watch: true,
});
+
+ await harness.executeWithCases([
+ async ({ result }) => {
+ expect(result?.success).toBeTrue();
+ const response = await fetch(new URL('ngsw.json', `${result?.baseUrl}`));
+ const { hashTable } = (await response.json()) as { hashTable: object };
+ const hashTableEntries = Object.keys(hashTable);
+
+ expect(hashTableEntries).toEqual([
+ '/assets/folder-asset.txt',
+ '/favicon.ico',
+ '/index.html',
+ '/media/spectrum.png',
+ ]);
+
+ await harness.writeFile(
+ 'src/assets/folder-new-asset.txt',
+ harness.readFile('src/assets/folder-asset.txt'),
+ );
+ },
+ async ({ result }) => {
+ expect(result?.success).toBeTrue();
+ const response = await fetch(new URL('ngsw.json', `${result?.baseUrl}`));
+ const { hashTable } = (await response.json()) as { hashTable: object };
+ const hashTableEntries = Object.keys(hashTable);
+
+ expect(hashTableEntries).toEqual([
+ '/assets/folder-asset.txt',
+ '/assets/folder-new-asset.txt',
+ '/favicon.ico',
+ '/index.html',
+ '/media/spectrum.png',
+ ]);
+ },
+ ]);
});
- },
-);
+ });
+});
diff --git a/packages/angular/build/src/builders/dev-server/tests/jasmine-helpers.ts b/packages/angular/build/src/builders/dev-server/tests/jasmine-helpers.ts
index c5a73446cf5a..0781b3d875a7 100644
--- a/packages/angular/build/src/builders/dev-server/tests/jasmine-helpers.ts
+++ b/packages/angular/build/src/builders/dev-server/tests/jasmine-helpers.ts
@@ -12,6 +12,7 @@ import { readFileSync } from 'node:fs';
import { JasmineBuilderHarness, host, setupApplicationTarget } from './setup';
const optionSchemaCache = new Map();
+let counter = 0;
export function describeServeBuilder(
builderHandler: BuilderHandlerFn,
@@ -19,7 +20,6 @@ export function describeServeBuilder(
specDefinitions: (
harness: JasmineBuilderHarness,
setupTarget: typeof setupApplicationTarget,
- isViteRun: true,
) => void,
): void {
let optionSchema = optionSchemaCache.get(options.schemaPath);
@@ -32,10 +32,11 @@ export function describeServeBuilder(
optionSchema,
});
- describe(options.name || builderHandler.name, () => {
+ // The counter is needed to avoid duplicate describe names as they are not allowed.
+ describe((options.name || builderHandler.name) + ` (${counter++})`, () => {
beforeEach(() => host.initialize().toPromise());
afterEach(() => host.restore().toPromise());
- specDefinitions(harness, setupApplicationTarget, true);
+ specDefinitions(harness, setupApplicationTarget);
});
}
diff --git a/packages/angular/build/src/builders/dev-server/tests/options/port_spec.ts b/packages/angular/build/src/builders/dev-server/tests/options/port_spec.ts
index 83f3a4d1486b..d3e860023bde 100644
--- a/packages/angular/build/src/builders/dev-server/tests/options/port_spec.ts
+++ b/packages/angular/build/src/builders/dev-server/tests/options/port_spec.ts
@@ -26,87 +26,77 @@ function getResultPort(result: Record | undefined): string | un
}
}
-describeServeBuilder(
- executeDevServer,
- DEV_SERVER_BUILDER_INFO,
- (harness, setupTarget, isViteRun) => {
- describe('option: "port"', () => {
- beforeEach(async () => {
- setupTarget(harness);
-
- // Application code is not needed for these tests
- await harness.writeFile('src/main.ts', '');
- });
+describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupTarget) => {
+ describe('option: "port"', () => {
+ beforeEach(async () => {
+ setupTarget(harness);
- it('uses default port (4200) when not present', async () => {
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- // Base options set port to zero
- port: undefined,
- });
+ // Application code is not needed for these tests
+ await harness.writeFile('src/main.ts', '');
+ });
- const { result, response, logs } = await executeOnceAndFetch(harness, '/');
+ it('uses default port (4200) when not present', async () => {
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ // Base options set port to zero
+ port: undefined,
+ });
- expect(result?.success).toBeTrue();
- expect(getResultPort(result)).toBe('4200');
- expect(await response?.text()).toContain('');
+ const { result, response } = await executeOnceAndFetch(harness, '/');
- if (!isViteRun) {
- expect(logs).toContain(
- jasmine.objectContaining({
- message: jasmine.stringMatching(/:4200/),
- }),
- );
- }
+ expect(result?.success).toBeTrue();
+ expect(getResultPort(result)).toBe('4200');
+ expect(await response?.text()).toContain('');
+ });
+
+ it('uses a random free port when set to 0 (zero)', async () => {
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ port: 0,
});
- it('uses a random free port when set to 0 (zero)', async () => {
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- port: 0,
- });
+ const { result, response } = await executeOnceAndFetch(harness, '/');
- const { result, response, logs } = await executeOnceAndFetch(harness, '/');
+ expect(result?.success).toBeTrue();
+ const port = getResultPort(result);
+ expect(port).not.toBe('4200');
+ // Should not be default Vite port either
+ expect(port).not.toBe('5173');
- expect(result?.success).toBeTrue();
- const port = getResultPort(result);
- expect(port).not.toBe('4200');
- if (isViteRun) {
- // Should not be default Vite port either
- expect(port).not.toBe('5173');
- }
-
- expect(port).toMatch(/\d{4,6}/);
- expect(await response?.text()).toContain('');
+ expect(port).toMatch(/\d{4,6}/);
+ expect(await response?.text()).toContain('');
+ });
- if (!isViteRun) {
- expect(logs).toContain(
- jasmine.objectContaining({
- message: jasmine.stringMatching(':' + port),
- }),
- );
- }
+ it('uses specific port when a non-zero number is specified', async () => {
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ port: 8000,
});
- it('uses specific port when a non-zero number is specified', async () => {
+ const { result, response } = await executeOnceAndFetch(harness, '/');
+
+ expect(result?.success).toBeTrue();
+ expect(getResultPort(result)).toBe('8000');
+ expect(await response?.text()).toContain('');
+ });
+
+ it('should be overwritten by process.env.PORT if it exists', async () => {
+ try {
harness.useTarget('serve', {
...BASE_OPTIONS,
port: 8000,
});
- const { result, response, logs } = await executeOnceAndFetch(harness, '/');
+ process.env.PORT = '4201';
+
+ const { result, response } = await executeOnceAndFetch(harness, '/');
expect(result?.success).toBeTrue();
- expect(getResultPort(result)).toBe('8000');
+ expect(getResultPort(result)).toBe('4201');
expect(await response?.text()).toContain('');
- if (!isViteRun) {
- expect(logs).toContain(
- jasmine.objectContaining({
- message: jasmine.stringMatching(':8000'),
- }),
- );
- }
- });
+ } finally {
+ delete process.env.PORT;
+ }
});
- },
-);
+ });
+});
diff --git a/packages/angular/build/src/builders/dev-server/tests/options/prebundle_spec.ts b/packages/angular/build/src/builders/dev-server/tests/options/prebundle_spec.ts
index 1e7c5fcd7322..80bab96d3bb8 100644
--- a/packages/angular/build/src/builders/dev-server/tests/options/prebundle_spec.ts
+++ b/packages/angular/build/src/builders/dev-server/tests/options/prebundle_spec.ts
@@ -12,88 +12,84 @@ import { describeServeBuilder } from '../jasmine-helpers';
import { BASE_OPTIONS, DEV_SERVER_BUILDER_INFO } from '../setup';
// TODO: Temporarily disabled pending investigation into test-only Vite not stopping when caching is enabled
-describeServeBuilder(
- executeDevServer,
- DEV_SERVER_BUILDER_INFO,
- (harness, setupTarget, isViteRun) => {
- // prebundling is not available in webpack
- (isViteRun ? xdescribe : xdescribe)('option: "prebundle"', () => {
- beforeEach(async () => {
- setupTarget(harness);
-
- harness.useProject('test', {
- cli: {
- cache: {
- enabled: true,
- },
+describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupTarget) => {
+ // prebundling is not available in webpack
+ xdescribe('option: "prebundle"', () => {
+ beforeEach(async () => {
+ setupTarget(harness);
+
+ harness.useProject('test', {
+ cli: {
+ cache: {
+ enabled: true,
},
- });
+ },
+ });
- // Application code is not needed for these tests
- await harness.writeFile(
- 'src/main.ts',
- `
+ // Application code is not needed for these tests
+ await harness.writeFile(
+ 'src/main.ts',
+ `
import { VERSION as coreVersion } from '@angular/core';
import { VERSION as platformVersion } from '@angular/platform-browser';
console.log(coreVersion);
console.log(platformVersion);
`,
- );
+ );
+ });
+
+ it('should prebundle dependencies when option is not present', async () => {
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
});
- it('should prebundle dependencies when option is not present', async () => {
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- });
+ const { result, content } = await executeOnceAndFetch(harness, '/main.js');
- const { result, content } = await executeOnceAndFetch(harness, '/main.js');
+ expect(result?.success).toBeTrue();
+ expect(content).toContain('vite/deps/@angular_core.js');
+ expect(content).not.toContain('node_modules/@angular/core/');
+ });
- expect(result?.success).toBeTrue();
- expect(content).toContain('vite/deps/@angular_core.js');
- expect(content).not.toContain('node_modules/@angular/core/');
+ it('should prebundle dependencies when option is set to true', async () => {
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ prebundle: true,
});
- it('should prebundle dependencies when option is set to true', async () => {
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- prebundle: true,
- });
+ const { result, content } = await executeOnceAndFetch(harness, '/main.js');
- const { result, content } = await executeOnceAndFetch(harness, '/main.js');
+ expect(result?.success).toBeTrue();
+ expect(content).toContain('vite/deps/@angular_core.js');
+ expect(content).not.toContain('node_modules/@angular/core/');
+ });
- expect(result?.success).toBeTrue();
- expect(content).toContain('vite/deps/@angular_core.js');
- expect(content).not.toContain('node_modules/@angular/core/');
+ it('should not prebundle dependencies when option is set to false', async () => {
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ prebundle: false,
});
- it('should not prebundle dependencies when option is set to false', async () => {
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- prebundle: false,
- });
+ const { result, content } = await executeOnceAndFetch(harness, '/main.js');
- const { result, content } = await executeOnceAndFetch(harness, '/main.js');
+ expect(result?.success).toBeTrue();
+ expect(content).not.toContain('vite/deps/@angular_core.js');
+ expect(content).toContain('node_modules/@angular/core/');
+ });
- expect(result?.success).toBeTrue();
- expect(content).not.toContain('vite/deps/@angular_core.js');
- expect(content).toContain('node_modules/@angular/core/');
+ it('should not prebundle specified dependency if added to exclude list', async () => {
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ prebundle: { exclude: ['@angular/platform-browser'] },
});
- it('should not prebundle specified dependency if added to exclude list', async () => {
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- prebundle: { exclude: ['@angular/platform-browser'] },
- });
+ const { result, content } = await executeOnceAndFetch(harness, '/main.js');
- const { result, content } = await executeOnceAndFetch(harness, '/main.js');
-
- expect(result?.success).toBeTrue();
- expect(content).toContain('vite/deps/@angular_core.js');
- expect(content).not.toContain('node_modules/@angular/core/');
- expect(content).not.toContain('vite/deps/@angular_platform-browser.js');
- expect(content).toContain('node_modules/@angular/platform-browser/');
- });
+ expect(result?.success).toBeTrue();
+ expect(content).toContain('vite/deps/@angular_core.js');
+ expect(content).not.toContain('node_modules/@angular/core/');
+ expect(content).not.toContain('vite/deps/@angular_platform-browser.js');
+ expect(content).toContain('node_modules/@angular/platform-browser/');
});
- },
-);
+ });
+});
diff --git a/packages/angular/build/src/builders/dev-server/tests/options/proxy-config_spec.ts b/packages/angular/build/src/builders/dev-server/tests/options/proxy-config_spec.ts
index 78f3323b97cc..1c6dfb60ca9d 100644
--- a/packages/angular/build/src/builders/dev-server/tests/options/proxy-config_spec.ts
+++ b/packages/angular/build/src/builders/dev-server/tests/options/proxy-config_spec.ts
@@ -12,7 +12,7 @@ import { executeOnceAndFetch } from '../execute-fetch';
import { describeServeBuilder } from '../jasmine-helpers';
import { BASE_OPTIONS, DEV_SERVER_BUILDER_INFO, BuilderHarness } from '../setup';
-describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupTarget, isVite) => {
+describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupTarget) => {
describe('option: "proxyConfig"', () => {
beforeEach(async () => {
setupTarget(harness);
@@ -236,14 +236,51 @@ describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupT
}
});
- /**
- * ****************************************************************************************************
- * ********************************** Below only Vite specific tests **********************************
- * ****************************************************************************************************
- */
- if (isVite) {
- viteOnlyTests(harness);
- }
+ it('proxies support regexp as context', async () => {
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ proxyConfig: 'proxy.config.json',
+ });
+
+ const proxyServer = await createProxyServer();
+ try {
+ await harness.writeFiles({
+ 'proxy.config.json': `
+ { "^/api/.*": { "target": "http://127.0.0.1:${proxyServer.address.port}" } }
+ `,
+ });
+
+ const { result, response } = await executeOnceAndFetch(harness, '/api/test');
+
+ expect(result?.success).toBeTrue();
+ expect(await response?.text()).toContain('TEST_API_RETURN');
+ } finally {
+ await proxyServer.close();
+ }
+ });
+
+ it('proxies support negated regexp as context', async () => {
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ proxyConfig: 'proxy.config.json',
+ });
+
+ const proxyServer = await createProxyServer();
+ try {
+ await harness.writeFiles({
+ 'proxy.config.json': `
+ { "^\\/(?!something).*": { "target": "http://127.0.0.1:${proxyServer.address.port}" } }
+ `,
+ });
+
+ const { result, response } = await executeOnceAndFetch(harness, '/api/test');
+
+ expect(result?.success).toBeTrue();
+ expect(await response?.text()).toContain('TEST_API_RETURN');
+ } finally {
+ await proxyServer.close();
+ }
+ });
});
});
@@ -270,54 +307,3 @@ async function createProxyServer() {
close: () => new Promise((resolve) => proxyServer.close(() => resolve())),
};
}
-
-/**
- * Vite specific tests
- */
-function viteOnlyTests(harness: BuilderHarness): void {
- it('proxies support regexp as context', async () => {
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- proxyConfig: 'proxy.config.json',
- });
-
- const proxyServer = await createProxyServer();
- try {
- await harness.writeFiles({
- 'proxy.config.json': `
- { "^/api/.*": { "target": "http://127.0.0.1:${proxyServer.address.port}" } }
- `,
- });
-
- const { result, response } = await executeOnceAndFetch(harness, '/api/test');
-
- expect(result?.success).toBeTrue();
- expect(await response?.text()).toContain('TEST_API_RETURN');
- } finally {
- await proxyServer.close();
- }
- });
-
- it('proxies support negated regexp as context', async () => {
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- proxyConfig: 'proxy.config.json',
- });
-
- const proxyServer = await createProxyServer();
- try {
- await harness.writeFiles({
- 'proxy.config.json': `
- { "^\\/(?!something).*": { "target": "http://127.0.0.1:${proxyServer.address.port}" } }
- `,
- });
-
- const { result, response } = await executeOnceAndFetch(harness, '/api/test');
-
- expect(result?.success).toBeTrue();
- expect(await response?.text()).toContain('TEST_API_RETURN');
- } finally {
- await proxyServer.close();
- }
- });
-}
diff --git a/packages/angular/build/src/builders/dev-server/tests/options/serve-path_spec.ts b/packages/angular/build/src/builders/dev-server/tests/options/serve-path_spec.ts
index 7570175c65d2..5917dcc8eeb4 100644
--- a/packages/angular/build/src/builders/dev-server/tests/options/serve-path_spec.ts
+++ b/packages/angular/build/src/builders/dev-server/tests/options/serve-path_spec.ts
@@ -12,109 +12,105 @@ import { executeOnceAndFetch } from '../execute-fetch';
import { describeServeBuilder } from '../jasmine-helpers';
import { BASE_OPTIONS, DEV_SERVER_BUILDER_INFO } from '../setup';
-describeServeBuilder(
- executeDevServer,
- DEV_SERVER_BUILDER_INFO,
- (harness, setupTarget, isViteRun) => {
- describe('option: "servePath"', () => {
- beforeEach(async () => {
- setupTarget(harness, {
- assets: ['src/assets'],
- });
-
- // Application code is not needed for these tests
- await harness.writeFile('src/main.ts', 'console.log("foo");');
+describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupTarget) => {
+ describe('option: "servePath"', () => {
+ beforeEach(async () => {
+ setupTarget(harness, {
+ assets: ['src/assets'],
});
- it('serves application at the root when option is not present', async () => {
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- });
-
- const { result, response } = await executeOnceAndFetch(harness, '/main.js');
+ // Application code is not needed for these tests
+ await harness.writeFile('src/main.ts', 'console.log("foo");');
+ });
- expect(result?.success).toBeTrue();
- const baseUrl = new URL(`${result?.baseUrl}`);
- expect(baseUrl.pathname).toBe('/');
- expect(await response?.text()).toContain('console.log');
+ it('serves application at the root when option is not present', async () => {
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
});
- it('serves application at specified path when option is used', async () => {
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- servePath: 'test',
- });
+ const { result, response } = await executeOnceAndFetch(harness, '/main.js');
- const { result, response } = await executeOnceAndFetch(harness, '/test/main.js');
+ expect(result?.success).toBeTrue();
+ const baseUrl = new URL(`${result?.baseUrl}`);
+ expect(baseUrl.pathname).toBe('/');
+ expect(await response?.text()).toContain('console.log');
+ });
- expect(result?.success).toBeTrue();
- const baseUrl = new URL(`${result?.baseUrl}/`);
- expect(baseUrl.pathname).toBe('/test/');
- expect(await response?.text()).toContain('console.log');
+ it('serves application at specified path when option is used', async () => {
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ servePath: 'test',
});
- // TODO(fix-vite): currently this is broken in vite.
- (isViteRun ? xit : it)('does not rewrite from root when option is used', async () => {
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- servePath: 'test',
- });
+ const { result, response } = await executeOnceAndFetch(harness, '/test/main.js');
- const { result, response } = await executeOnceAndFetch(harness, '/', {
- // fallback processing requires an accept header
- request: { headers: { accept: 'text/html' } },
- });
+ expect(result?.success).toBeTrue();
+ const baseUrl = new URL(`${result?.baseUrl}/`);
+ expect(baseUrl.pathname).toBe('/test/');
+ expect(await response?.text()).toContain('console.log');
+ });
- expect(result?.success).toBeTrue();
- expect(response?.status).toBe(404);
+ // TODO(fix-vite): currently this is broken in vite.
+ xit('does not rewrite from root when option is used', async () => {
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ servePath: 'test',
});
- it('does not rewrite from path outside serve path when option is used', async () => {
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- servePath: 'test',
- });
+ const { result, response } = await executeOnceAndFetch(harness, '/', {
+ // fallback processing requires an accept header
+ request: { headers: { accept: 'text/html' } },
+ });
- const { result, response } = await executeOnceAndFetch(harness, '/api/', {
- // fallback processing requires an accept header
- request: { headers: { accept: 'text/html' } },
- });
+ expect(result?.success).toBeTrue();
+ expect(response?.status).toBe(404);
+ });
- expect(result?.success).toBeTrue();
- expect(response?.status).toBe(404);
+ it('does not rewrite from path outside serve path when option is used', async () => {
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ servePath: 'test',
});
- it('rewrites from path inside serve path when option is used', async () => {
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- servePath: 'test',
- });
+ const { result, response } = await executeOnceAndFetch(harness, '/api/', {
+ // fallback processing requires an accept header
+ request: { headers: { accept: 'text/html' } },
+ });
- const { result, response } = await executeOnceAndFetch(harness, '/test/inside', {
- // fallback processing requires an accept header
- request: { headers: { accept: 'text/html' } },
- });
+ expect(result?.success).toBeTrue();
+ expect(response?.status).toBe(404);
+ });
+
+ it('rewrites from path inside serve path when option is used', async () => {
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ servePath: 'test',
+ });
- expect(result?.success).toBeTrue();
- expect(await response?.text()).toContain('');
+ const { result, response } = await executeOnceAndFetch(harness, '/test/inside', {
+ // fallback processing requires an accept header
+ request: { headers: { accept: 'text/html' } },
});
- it('serves assets at specified path when option is used', async () => {
- await harness.writeFile('src/assets/test.txt', 'hello world!');
+ expect(result?.success).toBeTrue();
+ expect(await response?.text()).toContain('');
+ });
- harness.useTarget('serve', {
- ...BASE_OPTIONS,
- servePath: 'test',
- });
+ it('serves assets at specified path when option is used', async () => {
+ await harness.writeFile('src/assets/test.txt', 'hello world!');
- const { result, response } = await executeOnceAndFetch(harness, '/test/assets/test.txt', {
- // fallback processing requires an accept header
- request: { headers: { accept: 'text/html' } },
- });
+ harness.useTarget('serve', {
+ ...BASE_OPTIONS,
+ servePath: 'test',
+ });
- expect(result?.success).toBeTrue();
- expect(await response?.text()).toContain('hello world');
+ const { result, response } = await executeOnceAndFetch(harness, '/test/assets/test.txt', {
+ // fallback processing requires an accept header
+ request: { headers: { accept: 'text/html' } },
});
+
+ expect(result?.success).toBeTrue();
+ expect(await response?.text()).toContain('hello world');
});
- },
-);
+ });
+});
diff --git a/packages/angular/build/src/builders/dev-server/vite/index.ts b/packages/angular/build/src/builders/dev-server/vite/index.ts
index 8129daac1ba1..557b2d34b52a 100644
--- a/packages/angular/build/src/builders/dev-server/vite/index.ts
+++ b/packages/angular/build/src/builders/dev-server/vite/index.ts
@@ -9,7 +9,6 @@
import type { BuilderContext } from '@angular-devkit/architect';
import type { Plugin } from 'esbuild';
import assert from 'node:assert';
-import { builtinModules, isBuiltin } from 'node:module';
import { join } from 'node:path';
import type { Connect, ViteDevServer } from 'vite';
import type { ComponentStyleRecord } from '../../../tools/vite/middlewares';
@@ -21,7 +20,6 @@ import { Result, ResultKind } from '../../application/results';
import { OutputHashing } from '../../application/schema';
import {
type ApplicationBuilderInternalOptions,
- type ExternalResultMetadata,
JavaScriptTransformer,
getSupportedBrowsers,
isZonelessApp,
@@ -99,8 +97,20 @@ export async function* serveWithVite(
browserOptions.ssr ||= true;
}
- // Disable auto CSP.
+ // Vite allowedHost syntax doesn't allow `*.` but `.` acts as `*.`
+ // Angular SSR supports `*.`.
+ const allowedHosts = Array.isArray(serverOptions.allowedHosts)
+ ? serverOptions.allowedHosts.map((host) => (host[0] === '.' ? '*' + host : host))
+ : serverOptions.allowedHosts === true
+ ? ['*']
+ : [];
+
+ // Always allow the dev server host
+ allowedHosts.push(serverOptions.host);
+
browserOptions.security = {
+ allowedHosts,
+ // Disable auto CSP.
autoCsp: false,
};
diff --git a/packages/angular/build/src/builders/karma/find-tests_spec.ts b/packages/angular/build/src/builders/karma/find-tests_spec.ts
deleted file mode 100644
index 88c97c8575fe..000000000000
--- a/packages/angular/build/src/builders/karma/find-tests_spec.ts
+++ /dev/null
@@ -1,97 +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 { getTestEntrypoints } from './find-tests';
-
-const UNIX_ENTRYPOINTS_OPTIONS = {
- pathSeparator: '/',
- workspaceRoot: '/my/workspace/root',
- projectSourceRoot: '/my/workspace/root/src-root',
-};
-
-const WINDOWS_ENTRYPOINTS_OPTIONS = {
- pathSeparator: '\\',
- workspaceRoot: 'C:\\my\\workspace\\root',
- projectSourceRoot: 'C:\\my\\workspace\\root\\src-root',
-};
-
-describe('getTestEntrypoints', () => {
- for (const options of [UNIX_ENTRYPOINTS_OPTIONS, WINDOWS_ENTRYPOINTS_OPTIONS]) {
- describe(`with path separator "${options.pathSeparator}"`, () => {
- function joinWithSeparator(base: string, rel: string) {
- return `${base}${options.pathSeparator}${rel.replace(/\//g, options.pathSeparator)}`;
- }
-
- function getEntrypoints(workspaceRelative: string[], sourceRootRelative: string[] = []) {
- return getTestEntrypoints(
- [
- ...workspaceRelative.map((p) => joinWithSeparator(options.workspaceRoot, p)),
- ...sourceRootRelative.map((p) => joinWithSeparator(options.projectSourceRoot, p)),
- ],
- options,
- );
- }
-
- it('returns an empty map without test files', () => {
- expect(getEntrypoints([])).toEqual(new Map());
- });
-
- it('strips workspace root and/or project source root', () => {
- expect(getEntrypoints(['a/b.spec.js'], ['c/d.spec.js'])).toEqual(
- new Map([
- ['spec-a-b.spec', joinWithSeparator(options.workspaceRoot, 'a/b.spec.js')],
- ['spec-c-d.spec', joinWithSeparator(options.projectSourceRoot, 'c/d.spec.js')],
- ]),
- );
- });
-
- it('adds unique prefixes to distinguish between similar names', () => {
- expect(getEntrypoints(['a/b/c/d.spec.js', 'a-b/c/d.spec.js'], ['a/b-c/d.spec.js'])).toEqual(
- new Map([
- ['spec-a-b-c-d.spec', joinWithSeparator(options.workspaceRoot, 'a/b/c/d.spec.js')],
- ['spec-a-b-c-d-2.spec', joinWithSeparator(options.workspaceRoot, 'a-b/c/d.spec.js')],
- [
- 'spec-a-b-c-d-3.spec',
- joinWithSeparator(options.projectSourceRoot, 'a/b-c/d.spec.js'),
- ],
- ]),
- );
- });
-
- describe('with removeTestExtension enabled', () => {
- function getEntrypoints(workspaceRelative: string[], sourceRootRelative: string[] = []) {
- return getTestEntrypoints(
- [
- ...workspaceRelative.map((p) => joinWithSeparator(options.workspaceRoot, p)),
- ...sourceRootRelative.map((p) => joinWithSeparator(options.projectSourceRoot, p)),
- ],
- { ...options, removeTestExtension: true },
- );
- }
-
- it('removes .spec extension', () => {
- expect(getEntrypoints(['a/b.spec.js'], ['c/d.spec.js'])).toEqual(
- new Map([
- ['spec-a-b', joinWithSeparator(options.workspaceRoot, 'a/b.spec.js')],
- ['spec-c-d', joinWithSeparator(options.projectSourceRoot, 'c/d.spec.js')],
- ]),
- );
- });
-
- it('removes .test extension', () => {
- expect(getEntrypoints(['a/b.test.js'], ['c/d.test.js'])).toEqual(
- new Map([
- ['spec-a-b', joinWithSeparator(options.workspaceRoot, 'a/b.test.js')],
- ['spec-c-d', joinWithSeparator(options.projectSourceRoot, 'c/d.test.js')],
- ]),
- );
- });
- });
- });
- }
-});
diff --git a/packages/angular/build/src/builders/karma/progress-reporter.ts b/packages/angular/build/src/builders/karma/progress-reporter.ts
index 908f1c856e6d..16824badd095 100644
--- a/packages/angular/build/src/builders/karma/progress-reporter.ts
+++ b/packages/angular/build/src/builders/karma/progress-reporter.ts
@@ -53,11 +53,9 @@ export function injectKarmaReporter(
private startWatchingBuild() {
void (async () => {
// This is effectively "for await of but skip what's already consumed".
- let isDone = false; // to mark the loop condition as "not constant".
- while (!isDone) {
+ while (true) {
const { done, value: buildOutput } = await buildIterator.next();
if (done) {
- isDone = true;
break;
}
diff --git a/packages/angular/build/src/builders/karma/tests/behavior/fake-async_spec.ts b/packages/angular/build/src/builders/karma/tests/behavior/fake-async_spec.ts
index 16da8daf2f55..12f43e47cce5 100644
--- a/packages/angular/build/src/builders/karma/tests/behavior/fake-async_spec.ts
+++ b/packages/angular/build/src/builders/karma/tests/behavior/fake-async_spec.ts
@@ -18,12 +18,13 @@ describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget) => {
it('loads zone.js/testing at the right time', async () => {
await harness.writeFiles({
'./src/app/app.component.ts': `
- import { Component } from '@angular/core';
+ import { ChangeDetectionStrategy, Component } from '@angular/core';
@Component({
selector: 'app-root',
standalone: false,
template: '',
+ changeDetection: ChangeDetectionStrategy.Eager
})
export class AppComponent {
message = 'Initial';
diff --git a/packages/angular/build/src/builders/karma/tests/options/code-coverage_spec.ts b/packages/angular/build/src/builders/karma/tests/options/code-coverage_spec.ts
index a8849ba643ef..21dbfdfc073d 100644
--- a/packages/angular/build/src/builders/karma/tests/options/code-coverage_spec.ts
+++ b/packages/angular/build/src/builders/karma/tests/options/code-coverage_spec.ts
@@ -88,19 +88,15 @@ describeKarmaBuilder(execute, KARMA_BUILDER_INFO, (harness, setupTarget) => {
}
`,
});
- await harness.modifyFile('tsconfig.json', (content) =>
- content.replace(
- /"baseUrl": ".\/",/,
- `
- "baseUrl": "./",
- "paths": {
- "my-lib": [
- "./dist/my-lib"
- ]
- },
- `,
- ),
- );
+ await harness.modifyFile('tsconfig.json', (content) => {
+ const tsconfig = JSON.parse(content);
+ tsconfig.compilerOptions ??= {};
+ tsconfig.compilerOptions.paths = {
+ 'my-lib': ['./dist/my-lib'],
+ };
+
+ return JSON.stringify(tsconfig, null, 2);
+ });
harness.useTarget('test', {
...BASE_OPTIONS,
diff --git a/packages/angular/build/src/builders/karma/tests/setup.ts b/packages/angular/build/src/builders/karma/tests/setup.ts
index e6a013946904..6292074daaf6 100644
--- a/packages/angular/build/src/builders/karma/tests/setup.ts
+++ b/packages/angular/build/src/builders/karma/tests/setup.ts
@@ -112,6 +112,8 @@ export async function setupApplicationTarget(
await harness.appendToFile('src/polyfills.ts', `import '@angular/localize/init';`);
}
+let counter = 0;
+
/** Runs the test against both an application- and a browser-builder context. */
export function describeKarmaBuilder(
builderHandler: BuilderHandlerFn,
@@ -128,7 +130,7 @@ export function describeKarmaBuilder(
optionSchema,
});
- describe(options.name || builderHandler.name, () => {
+ describe((options.name || builderHandler.name) + ` (${counter++})`, () => {
beforeEach(async () => {
await host.initialize().toPromise();
diff --git a/packages/angular/build/src/builders/unit-test/builder.ts b/packages/angular/build/src/builders/unit-test/builder.ts
index 6f2edd0281d7..97c3c0dce055 100644
--- a/packages/angular/build/src/builders/unit-test/builder.ts
+++ b/packages/angular/build/src/builders/unit-test/builder.ts
@@ -45,11 +45,11 @@ async function loadTestRunner(runnerName: string): Promise {
} catch (e) {
assertIsError(e);
if (e.code === 'ERR_MODULE_NOT_FOUND') {
- throw new Error(`Unknown test runner "${runnerName}".`);
+ throw new Error(`Unknown test runner "${runnerName}".`, { cause: e });
}
throw new Error(
- `Failed to load the '${runnerName}' test runner. The package may be corrupted or improperly installed.\n` +
- `Error: ${e.message}`,
+ `Failed to load the '${runnerName}' test runner. The package may be corrupted or improperly installed.`,
+ { cause: e },
);
}
@@ -370,19 +370,20 @@ async function transformNgPackagrOptions(
ngPackageJson = JSON.parse(await readFile(ngPackagePath, 'utf-8'));
} catch (e) {
assertIsError(e);
- throw new Error(`Could not read ng-package.json at ${ngPackagePath}: ${e.message}`);
+ throw new Error(`Could not read ng-package.json at ${ngPackagePath}`, {
+ cause: e,
+ });
}
- const lib = ngPackageJson['lib'] || {};
- const styleIncludePaths = lib['styleIncludePaths'] || [];
- const assets = ngPackageJson['assets'] || [];
- const inlineStyleLanguage = ngPackageJson['inlineStyleLanguage'];
+ const { lib: { styleIncludePaths = [] } = {}, assets = [], inlineStyleLanguage } = ngPackageJson;
+
+ const includePaths = styleIncludePaths.map((includePath: string) =>
+ path.resolve(path.dirname(ngPackagePath), includePath),
+ );
return {
- stylePreprocessorOptions: styleIncludePaths.length
- ? { includePaths: styleIncludePaths }
- : undefined,
+ stylePreprocessorOptions: includePaths.length ? { includePaths } : undefined,
assets: assets.length ? assets : undefined,
- inlineStyleLanguage: typeof inlineStyleLanguage === 'string' ? inlineStyleLanguage : undefined,
+ inlineStyleLanguage,
} as ApplicationBuilderInternalOptions;
}
diff --git a/packages/angular/build/src/builders/unit-test/options.ts b/packages/angular/build/src/builders/unit-test/options.ts
index 72b5f3eb3e8d..0a71f2d642f1 100644
--- a/packages/angular/build/src/builders/unit-test/options.ts
+++ b/packages/angular/build/src/builders/unit-test/options.ts
@@ -116,7 +116,7 @@ export async function normalizeOptions(
buildProgress: progress,
reporters: normalizeReporterOption(options.reporters),
outputFile: options.outputFile,
- browsers,
+ browsers: browsers?.length ? browsers : undefined,
browserViewport: width && height ? { width, height } : undefined,
watch,
debug: options.debug ?? false,
diff --git a/packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider.ts b/packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider.ts
index f89848abfbf6..0ca80f4fa60f 100644
--- a/packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider.ts
+++ b/packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider.ts
@@ -7,7 +7,11 @@
*/
import { createRequire } from 'node:module';
-import type { BrowserBuiltinProvider, BrowserConfigOptions } from 'vitest/node';
+import type {
+ BrowserBuiltinProvider,
+ BrowserConfigOptions,
+ BrowserProviderOption,
+} from 'vitest/node';
import { assertIsError } from '../../../../utils/error';
export interface BrowserConfiguration {
@@ -37,7 +41,13 @@ function findBrowserProvider(
return undefined;
}
-function normalizeBrowserName(browserName: string): { browser: string; headless: boolean } {
+export interface BrowserInstanceConfiguration {
+ browser: string;
+ headless: boolean;
+ provider?: BrowserProviderOption;
+}
+
+function normalizeBrowserName(browserName: string): BrowserInstanceConfiguration {
// Normalize browser names to match Vitest's expectations for headless but also supports karma's names
// e.g., 'ChromeHeadless' -> 'chrome', 'FirefoxHeadless' -> 'firefox'
// and 'Chrome' -> 'chrome', 'Firefox' -> 'firefox'.
@@ -50,6 +60,67 @@ function normalizeBrowserName(browserName: string): { browser: string; headless:
};
}
+/**
+ * Mutates the provided browser instances to apply standard headless execution
+ * constraints based on the chosen provider, user options, and CI environment presence.
+ *
+ * @param instances The normalized browser instances to mutate.
+ * @param providerName The identifier for the chosen Vitest browser provider.
+ * @param headless The user-provided headless configuration option.
+ * @param isCI Whether the current environment is running in CI.
+ * @returns An array of informational messages generated during evaluation.
+ */
+export function applyHeadlessConfiguration(
+ instances: BrowserInstanceConfiguration[],
+ providerName: BrowserBuiltinProvider | undefined,
+ headless: boolean | undefined,
+ isCI: boolean,
+): string[] {
+ const messages: string[] = [];
+
+ if (providerName === 'preview') {
+ instances.forEach((instance) => {
+ // Preview mode only supports headed execution
+ instance.headless = false;
+ });
+
+ if (headless) {
+ messages.push('The "headless" option is ignored when using the "preview" provider.');
+ }
+ } else if (headless !== undefined) {
+ if (headless) {
+ const allHeadlessByDefault = isCI || instances.every((i) => i.headless);
+ if (allHeadlessByDefault) {
+ messages.push(
+ 'The "headless" option is unnecessary as all browsers are already configured to run in headless mode.',
+ );
+ }
+ }
+
+ instances.forEach((instance) => {
+ instance.headless = headless;
+ });
+ } else if (isCI) {
+ instances.forEach((instance) => {
+ instance.headless = true;
+ });
+ }
+
+ return messages;
+}
+
+/**
+ * Resolves and configures the Vitest browser provider for the unit test builder.
+ * Dynamically discovers and imports the necessary provider (Playwright, WebdriverIO, or Preview),
+ * maps the requested browser instances, and applies environment-specific execution logic.
+ *
+ * @param browsers An array of requested browser names (e.g., 'chrome', 'firefox').
+ * @param headless User-provided configuration for headless execution.
+ * @param debug Whether the builder is running in watch or debug mode.
+ * @param projectSourceRoot The root directory of the project being tested for resolving installed packages.
+ * @param viewport Optional viewport dimensions to apply to the launched browser instances.
+ * @returns A fully resolved Vitest browser configuration object alongside any generated warning or error messages.
+ */
export async function setupBrowserConfiguration(
browsers: string[] | undefined,
headless: boolean | undefined,
@@ -79,6 +150,8 @@ export async function setupBrowserConfiguration(
);
}
+ const instances = browsers.map(normalizeBrowserName);
+
let provider: import('vitest/node').BrowserProviderOption | undefined;
if (providerName) {
const providerPackage = `@vitest/browser-${providerName}`;
@@ -88,29 +161,27 @@ export async function setupBrowserConfiguration(
// Validate that the imported module has the expected structure
const providerFactory = providerModule[providerName];
if (typeof providerFactory === 'function') {
- if (
- providerName === 'playwright' &&
- process.env['CHROME_BIN']?.includes('rules_browsers')
- ) {
- // Use the Chrome binary from the 'rules_browsers' toolchain (via CHROME_BIN)
- // for Playwright when available to ensure hermetic testing, preventing reliance
- // on locally installed or NPM-managed browser versions.
- provider = providerFactory({
- launchOptions: {
- executablePath: process.env.CHROME_BIN,
- },
+ if (providerName === 'playwright') {
+ const executablePath = process.env['CHROME_BIN'];
+ const baseOptions = {
contextOptions: {
// Enables `prefer-color-scheme` for Vitest browser instead of `light`
colorScheme: null,
},
- });
- } else if (providerName === 'playwright') {
- provider = providerFactory({
- contextOptions: {
- // Enables `prefer-color-scheme` for Vitest browser instead of `light`
- colorScheme: null,
- },
- });
+ };
+
+ provider = providerFactory(baseOptions);
+
+ if (executablePath) {
+ for (const instance of instances) {
+ if (instance.browser === 'chrome' || instance.browser === 'chromium') {
+ instance.provider = providerFactory({
+ ...baseOptions,
+ launchOptions: { executablePath },
+ });
+ }
+ }
+ }
} else {
provider = providerFactory();
}
@@ -143,36 +214,7 @@ export async function setupBrowserConfiguration(
}
const isCI = !!process.env['CI'];
- const instances = browsers.map(normalizeBrowserName);
- const messages: string[] = [];
-
- if (providerName === 'preview') {
- instances.forEach((instance) => {
- // Preview mode only supports headed execution
- instance.headless = false;
- });
-
- if (headless) {
- messages.push('The "headless" option is ignored when using the "preview" provider.');
- }
- } else if (headless !== undefined) {
- if (headless) {
- const allHeadlessByDefault = isCI || instances.every((i) => i.headless);
- if (allHeadlessByDefault) {
- messages.push(
- 'The "headless" option is unnecessary as all browsers are already configured to run in headless mode.',
- );
- }
- }
-
- instances.forEach((instance) => {
- instance.headless = headless;
- });
- } else if (isCI) {
- instances.forEach((instance) => {
- instance.headless = true;
- });
- }
+ const messages = applyHeadlessConfiguration(instances, providerName, headless, isCI);
const browser = {
enabled: true,
diff --git a/packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider_spec.ts b/packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider_spec.ts
index 66f7254593b0..0dd0778420bd 100644
--- a/packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider_spec.ts
+++ b/packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider_spec.ts
@@ -9,7 +9,7 @@
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
-import { setupBrowserConfiguration } from './browser-provider';
+import { applyHeadlessConfiguration, setupBrowserConfiguration } from './browser-provider';
describe('setupBrowserConfiguration', () => {
let workspaceRoot: string;
@@ -47,8 +47,8 @@ describe('setupBrowserConfiguration', () => {
expect(browser?.enabled).toBeTrue();
expect(browser?.instances).toEqual([
- { browser: 'chrome', headless: true },
- { browser: 'firefox', headless: false },
+ jasmine.objectContaining({ browser: 'chrome', headless: true }),
+ jasmine.objectContaining({ browser: 'firefox', headless: false }),
]);
});
@@ -66,8 +66,8 @@ describe('setupBrowserConfiguration', () => {
);
expect(browser?.instances).toEqual([
- { browser: 'chrome', headless: true },
- { browser: 'firefox', headless: true },
+ jasmine.objectContaining({ browser: 'chrome', headless: true }),
+ jasmine.objectContaining({ browser: 'firefox', headless: true }),
]);
} finally {
if (originalCI === undefined) {
@@ -196,8 +196,8 @@ describe('setupBrowserConfiguration', () => {
);
expect(browser?.instances).toEqual([
- { browser: 'chrome', headless: true },
- { browser: 'firefox', headless: true },
+ jasmine.objectContaining({ browser: 'chrome', headless: true }),
+ jasmine.objectContaining({ browser: 'firefox', headless: true }),
]);
expect(messages).toEqual([]);
});
@@ -215,4 +215,106 @@ describe('setupBrowserConfiguration', () => {
'The "headless" option is unnecessary as all browsers are already configured to run in headless mode.',
]);
});
+
+ describe('CHROME_BIN usage', () => {
+ let originalChromeBin: string | undefined;
+
+ beforeEach(() => {
+ originalChromeBin = process.env['CHROME_BIN'];
+ process.env['CHROME_BIN'] = '/custom/path/to/chrome';
+ });
+
+ afterEach(() => {
+ if (originalChromeBin === undefined) {
+ delete process.env['CHROME_BIN'];
+ } else {
+ process.env['CHROME_BIN'] = originalChromeBin;
+ }
+ });
+
+ it('should set executablePath on the individual chrome instance', async () => {
+ const { browser } = await setupBrowserConfiguration(
+ ['ChromeHeadless', 'Chromium'],
+ undefined,
+ false,
+ workspaceRoot,
+ undefined,
+ );
+
+ // Verify the global provider does NOT have executablePath
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ expect((browser?.provider as any)?.options?.launchOptions?.executablePath).toBeUndefined();
+
+ // Verify the individual instances have executablePath
+ expect(
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (browser?.instances?.[0]?.provider as any)?.options?.launchOptions?.executablePath,
+ ).toBe('/custom/path/to/chrome');
+ expect(
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (browser?.instances?.[1]?.provider as any)?.options?.launchOptions?.executablePath,
+ ).toBe('/custom/path/to/chrome');
+ });
+
+ it('should set executablePath for chrome instances but not for others when mixed browsers are requested', async () => {
+ const { browser } = await setupBrowserConfiguration(
+ ['ChromeHeadless', 'Firefox'],
+ undefined,
+ false,
+ workspaceRoot,
+ undefined,
+ );
+
+ // Verify the global provider does NOT have executablePath
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ expect((browser?.provider as any)?.options?.launchOptions?.executablePath).toBeUndefined();
+
+ // Verify chrome gets it
+ expect(
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (browser?.instances?.[0]?.provider as any)?.options?.launchOptions?.executablePath,
+ ).toBe('/custom/path/to/chrome');
+
+ // Verify firefox does not
+ expect(browser?.instances?.[1]?.provider).toBeUndefined();
+ });
+ });
+
+ describe('applyHeadlessConfiguration', () => {
+ it('should set headless false and issue warning when using preview provider with headless true', () => {
+ const instances = [{ browser: 'chrome', headless: true }];
+ const messages = applyHeadlessConfiguration(instances, 'preview', true, false);
+
+ expect(instances[0].headless).toBeFalse();
+ expect(messages).toEqual([
+ 'The "headless" option is ignored when using the "preview" provider.',
+ ]);
+ });
+
+ it('should force headless mode when headless option is true', () => {
+ const instances = [{ browser: 'chrome', headless: false }];
+ const messages = applyHeadlessConfiguration(instances, 'playwright', true, false);
+
+ expect(instances[0].headless).toBeTrue();
+ expect(messages).toEqual([]);
+ });
+
+ it('should return information message when headless option is redundant', () => {
+ const instances = [{ browser: 'chrome', headless: true }];
+ const messages = applyHeadlessConfiguration(instances, 'playwright', true, false);
+
+ expect(instances[0].headless).toBeTrue();
+ expect(messages).toEqual([
+ 'The "headless" option is unnecessary as all browsers are already configured to run in headless mode.',
+ ]);
+ });
+
+ it('should force headless mode in CI environment when headless is undefined', () => {
+ const instances = [{ browser: 'chrome', headless: false }];
+ const messages = applyHeadlessConfiguration(instances, 'playwright', undefined, true);
+
+ expect(instances[0].headless).toBeTrue();
+ expect(messages).toEqual([]);
+ });
+ });
});
diff --git a/packages/angular/build/src/builders/unit-test/runners/vitest/build-options.ts b/packages/angular/build/src/builders/unit-test/runners/vitest/build-options.ts
index 3aa7e2c8947e..482878c520ca 100644
--- a/packages/angular/build/src/builders/unit-test/runners/vitest/build-options.ts
+++ b/packages/angular/build/src/builders/unit-test/runners/vitest/build-options.ts
@@ -6,21 +6,35 @@
* found in the LICENSE file at https://angular.dev/license
*/
+/**
+ * @fileoverview
+ * Provides Vitest-specific build options and virtual file contents for Angular unit testing.
+ */
+
+import { createRequire } from 'node:module';
import path from 'node:path';
import { toPosixPath } from '../../../../utils/path';
import type { ApplicationBuilderInternalOptions } from '../../../application/options';
import { OutputHashing } from '../../../application/schema';
-import { NormalizedUnitTestBuilderOptions, injectTestingPolyfills } from '../../options';
+import { NormalizedUnitTestBuilderOptions } from '../../options';
import { findTests, getTestEntrypoints } from '../../test-discovery';
import { RunnerOptions } from '../api';
+/**
+ * Creates the virtual file contents to initialize the Angular testing environment (TestBed).
+ *
+ * @param providersFile Optional path to a file that exports default providers.
+ * @param projectSourceRoot The root directory of the project source.
+ * @param teardown Whether to configure TestBed to destroy after each test.
+ * @param zoneTestingStrategy How zone.js should be loaded during initialization.
+ * @returns The string content of the virtual initialization file.
+ */
function createTestBedInitVirtualFile(
providersFile: string | undefined,
projectSourceRoot: string,
teardown: boolean,
- polyfills: string[] = [],
+ zoneTestingStrategy: 'none' | 'static' | 'dynamic',
): string {
- const usesZoneJS = polyfills.includes('zone.js');
let providersImport = 'const providers = [];';
if (providersFile) {
const relativePath = path.relative(projectSourceRoot, providersFile);
@@ -29,14 +43,27 @@ function createTestBedInitVirtualFile(
providersImport = `import providers from './${importPath}';`;
}
+ let zoneTestingSnippet = '';
+ if (zoneTestingStrategy === 'static') {
+ zoneTestingSnippet = `import 'zone.js/testing';`;
+ } else if (zoneTestingStrategy === 'dynamic') {
+ zoneTestingSnippet = `if (typeof Zone !== 'undefined') {
+ // 'zone.js/testing' is used to initialize the ZoneJS testing environment.
+ // It must be imported dynamically to avoid a static dependency on 'zone.js'.
+ await import('zone.js/testing');
+ }`;
+ }
+
return `
// Initialize the Angular testing environment
- import { NgModule${usesZoneJS ? ', provideZoneChangeDetection' : ''} } from '@angular/core';
+ import { NgModule, provideZoneChangeDetection } from '@angular/core';
import { getTestBed, ɵgetCleanupHook as getCleanupHook } from '@angular/core/testing';
import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing';
import { afterEach, beforeEach } from 'vitest';
${providersImport}
+ ${zoneTestingSnippet}
+
// The beforeEach and afterEach hooks are registered outside the globalThis guard.
// This ensures that the hooks are always applied, even in non-isolated browser environments.
// Same as https://github.com/angular/angular/blob/05a03d3f975771bb59c7eefd37c01fa127ee2229/packages/core/testing/srcs/test_hooks.ts#L21-L29
@@ -52,7 +79,10 @@ function createTestBedInitVirtualFile(
// The guard condition above ensures that the setup is only performed once.
@NgModule({
- providers: [${usesZoneJS ? 'provideZoneChangeDetection(), ' : ''}...providers],
+ providers: [
+ ...(typeof Zone !== 'undefined' ? [provideZoneChangeDetection()] : []),
+ ...providers,
+ ],
})
class TestModule {}
@@ -65,6 +95,13 @@ function createTestBedInitVirtualFile(
`;
}
+/**
+ * Adjusts output hashing settings for testing purposes. For example, ensuring media
+ * is continued to be hashed to avoid overwriting assets, but turning off JavaScript hashing.
+ *
+ * @param hashing The original OutputHashing configuration.
+ * @returns The adjusted OutputHashing configuration.
+ */
function adjustOutputHashing(hashing?: OutputHashing): OutputHashing {
switch (hashing) {
case OutputHashing.All:
@@ -76,6 +113,45 @@ function adjustOutputHashing(hashing?: OutputHashing): OutputHashing {
}
}
+/**
+ * Resolves the Zone.js testing strategy by inspecting polyfills and resolving zone.js package.
+ *
+ * @param buildOptions The partial application builder options.
+ * @param projectSourceRoot The root directory of the project source.
+ * @returns The resolved zone testing strategy ('none', 'static', 'dynamic').
+ */
+function getZoneTestingStrategy(
+ buildOptions: Partial,
+ projectSourceRoot: string,
+): 'none' | 'static' | 'dynamic' {
+ if (buildOptions.polyfills?.includes('zone.js/testing')) {
+ return 'none';
+ }
+
+ if (buildOptions.polyfills?.includes('zone.js')) {
+ return 'static';
+ }
+
+ try {
+ const projectRequire = createRequire(path.join(projectSourceRoot, 'package.json'));
+ projectRequire.resolve('zone.js');
+
+ return 'dynamic';
+ } catch {
+ return 'none';
+ }
+}
+
+/**
+ * Generates options and virtual files for the Vitest test runner.
+ *
+ * Discovers specs matchers, creates entry points, decides polyfills strategy, and orchestrates
+ * internal ApplicationBuilder options.
+ *
+ * @param options The normalized unit test builder options.
+ * @param baseBuildOptions The base build config to derive testing config from.
+ * @returns An async RunnerOptions configuration.
+ */
export async function getVitestBuildOptions(
options: NormalizedUnitTestBuilderOptions,
baseBuildOptions: Partial,
@@ -145,24 +221,49 @@ export async function getVitestBuildOptions(
externalDependencies,
};
- buildOptions.polyfills = injectTestingPolyfills(buildOptions.polyfills);
+ // Inject the zone.js testing polyfill if Zone.js is installed.
+ const zoneTestingStrategy = getZoneTestingStrategy(buildOptions, projectSourceRoot);
const testBedInitContents = createTestBedInitVirtualFile(
providersFile,
projectSourceRoot,
!options.debug,
- buildOptions.polyfills,
+ zoneTestingStrategy,
);
const mockPatchContents = `
import { vi } from 'vitest';
- const error = new Error(
- 'The "vi.mock" and related methods are not supported with the Angular unit-test system. Please use Angular TestBed for mocking.');
- vi.mock = () => { throw error; };
- vi.doMock = () => { throw error; };
- vi.importMock = () => { throw error; };
- vi.unmock = () => { throw error; };
- vi.doUnmock = () => { throw error; };
+
+ const ANGULAR_VITEST_MOCK_PATCH = Symbol.for('@angular/cli/vitest-mock-patch');
+ if (!globalThis[ANGULAR_VITEST_MOCK_PATCH]) {
+ globalThis[ANGULAR_VITEST_MOCK_PATCH] = true;
+
+ const error = new Error(
+ 'The "vi.mock" and related methods are not supported for relative imports with the Angular unit-test system. ' +
+ 'Please use Angular TestBed for mocking dependencies.'
+ );
+
+ // Store original implementations
+ const { mock, doMock, importMock, unmock, doUnmock } = vi;
+
+ function patch(original) {
+ return (path, ...args) => {
+ // Check if the path is a string and starts with a character that indicates a relative path.
+ if (typeof path === 'string' && /^[./]/.test(path)) {
+ throw error;
+ }
+
+ // Call the original function for non-relative paths.
+ return original(path, ...args);
+ };
+ }
+
+ vi.mock = patch(mock);
+ vi.doMock = patch(doMock);
+ vi.importMock = patch(importMock);
+ vi.unmock = patch(unmock);
+ vi.doUnmock = patch(doUnmock);
+ }
`;
return {
diff --git a/packages/angular/build/src/builders/unit-test/runners/vitest/executor.ts b/packages/angular/build/src/builders/unit-test/runners/vitest/executor.ts
index 503aa5da9071..c5b70e9a2487 100644
--- a/packages/angular/build/src/builders/unit-test/runners/vitest/executor.ts
+++ b/packages/angular/build/src/builders/unit-test/runners/vitest/executor.ts
@@ -27,6 +27,11 @@ import { setupBrowserConfiguration } from './browser-provider';
import { findVitestBaseConfig } from './configuration';
import { createVitestConfigPlugin, createVitestPlugins } from './plugins';
+enum DebugLogLevel {
+ Info = 1,
+ Verbose = 2,
+}
+
export class VitestExecutor implements TestExecutor {
private vitest: Vitest | undefined;
private normalizePath: ((id: string) => string) | undefined;
@@ -40,6 +45,7 @@ export class VitestExecutor implements TestExecutor {
explicitBrowser: [],
explicitServer: [],
};
+ private readonly debugLevel: number;
// This is a reverse map of the entry points created in `build-options.ts`.
// It is used by the in-memory provider plugin to map the requested test file
@@ -54,19 +60,42 @@ export class VitestExecutor implements TestExecutor {
testEntryPointMappings: Map | undefined,
logger: BuilderContext['logger'],
) {
+ const level = parseInt(process.env['NG_TEST_LOG'] ?? '0', 10);
+ this.debugLevel = isNaN(level) ? 0 : level;
+
this.projectName = projectName;
this.options = options;
this.logger = logger;
+ this.debugLog(DebugLogLevel.Info, 'VitestExecutor instantiated.');
+ this.debugLog(DebugLogLevel.Verbose, 'NormalizedUnitTestBuilderOptions:', options);
+
if (testEntryPointMappings) {
for (const [entryPoint, testFile] of testEntryPointMappings) {
this.testFileToEntryPoint.set(testFile, entryPoint);
this.entryPointToTestFile.set(entryPoint + '.js', testFile);
}
+ this.debugLog(
+ DebugLogLevel.Verbose,
+ 'Test entry point mappings:',
+ Object.fromEntries(testEntryPointMappings),
+ );
}
}
+ private debugLog(level: DebugLogLevel, message: string, data?: object) {
+ if (this.debugLevel < level) {
+ return;
+ }
+
+ const formattedMessage = `[VitestExecutor:${DebugLogLevel[level]}] ${message}`;
+ // Custom formatting for data object to ensure it's readable
+ const logData = data ? JSON.stringify(data, null, 2) : '';
+ this.logger.info(`${formattedMessage}${logData ? `\n${logData}` : ''}`);
+ }
+
async *execute(buildResult: FullResult | IncrementalResult): AsyncIterable {
+ this.debugLog(DebugLogLevel.Info, `Executing test run (kind: ${buildResult.kind}).`);
this.normalizePath ??= (await import('vite')).normalizePath;
if (buildResult.kind === ResultKind.Full) {
@@ -74,7 +103,20 @@ export class VitestExecutor implements TestExecutor {
for (const [path, file] of Object.entries(buildResult.files)) {
this.buildResultFiles.set(this.normalizePath(path), file);
}
+ this.debugLog(
+ DebugLogLevel.Info,
+ `Full build results received. Total files: ${this.buildResultFiles.size}.`,
+ );
} else {
+ this.debugLog(
+ DebugLogLevel.Info,
+ `Incremental build results received.` +
+ `Added: ${buildResult.added.length}, Modified: ${buildResult.modified.length}, Removed: ${buildResult.removed.length}.`,
+ );
+ this.debugLog(DebugLogLevel.Verbose, 'Added files:', buildResult.added);
+ this.debugLog(DebugLogLevel.Verbose, 'Modified files:', buildResult.modified);
+ this.debugLog(DebugLogLevel.Verbose, 'Removed files:', buildResult.removed);
+
for (const file of buildResult.removed) {
this.buildResultFiles.delete(this.normalizePath(file.path));
}
@@ -84,6 +126,7 @@ export class VitestExecutor implements TestExecutor {
}
updateExternalMetadata(buildResult, this.externalMetadata, undefined, true);
+ this.debugLog(DebugLogLevel.Verbose, 'Updated external metadata:', this.externalMetadata);
// Reset the exit code to allow for a clean state.
// This is necessary because Vitest may set the exit code on failure, which can
@@ -103,7 +146,16 @@ export class VitestExecutor implements TestExecutor {
// We need to find the original source file path to pass to Vitest.
const source = this.entryPointToTestFile.get(modifiedFile);
if (source) {
+ this.debugLog(
+ DebugLogLevel.Verbose,
+ `Mapped output file '${modifiedFile}' to source file '${source}' for re-run.`,
+ );
modifiedSourceFiles.add(source);
+ } else {
+ this.debugLog(
+ DebugLogLevel.Verbose,
+ `Could not map output file '${modifiedFile}' to a source file. It may not be a test file.`,
+ );
}
vitest.invalidateFile(
this.normalizePath(path.join(this.options.workspaceRoot, modifiedFile)),
@@ -120,7 +172,11 @@ export class VitestExecutor implements TestExecutor {
}
if (specsToRerun.length > 0) {
+ this.debugLog(DebugLogLevel.Info, `Re-running ${specsToRerun.length} test specifications.`);
+ this.debugLog(DebugLogLevel.Verbose, 'Specs to rerun:', specsToRerun);
testResults = await vitest.rerunTestSpecifications(specsToRerun);
+ } else {
+ this.debugLog(DebugLogLevel.Info, 'No test specifications to rerun.');
}
}
@@ -128,20 +184,29 @@ export class VitestExecutor implements TestExecutor {
const testModules = testResults?.testModules ?? this.vitest.state.getTestModules();
let success = testModules.every((testModule) => testModule.ok());
+ let finalResultReason = 'All tests passed.';
+
// Vitest does not return a failure result when coverage thresholds are not met.
// Instead, it sets the process exit code to 1.
// We check this exit code to determine if the test run should be considered a failure.
if (success && process.exitCode === 1) {
success = false;
+ finalResultReason = 'Test run failed due to unmet coverage thresholds.';
// Reset the exit code to prevent it from carrying over to subsequent runs/builds
process.exitCode = 0;
}
+ this.debugLog(
+ DebugLogLevel.Info,
+ `Test run finished with success: ${success}. Reason: ${finalResultReason}`,
+ );
yield { success };
}
async [Symbol.asyncDispose](): Promise {
+ this.debugLog(DebugLogLevel.Info, 'Disposing VitestExecutor: Closing Vitest instance.');
await this.vitest?.close();
+ this.debugLog(DebugLogLevel.Info, 'Vitest instance closed.');
}
private prepareSetupFiles(): string[] {
@@ -154,10 +219,13 @@ export class VitestExecutor implements TestExecutor {
testSetupFiles.unshift('polyfills.js');
}
+ this.debugLog(DebugLogLevel.Info, 'Prepared setup files:', testSetupFiles);
+
return testSetupFiles;
}
private async initializeVitest(): Promise {
+ this.debugLog(DebugLogLevel.Info, 'Initializing Vitest.');
const {
coverage,
reporters,
@@ -180,11 +248,16 @@ export class VitestExecutor implements TestExecutor {
vitestNodeModule = await import('vitest/node');
} catch (error: unknown) {
assertIsError(error);
+ this.debugLog(
+ DebugLogLevel.Info,
+ `Failed to import 'vitest/node'. Error code: ${error.code}`,
+ );
if (error.code !== 'ERR_MODULE_NOT_FOUND') {
throw error;
}
throw new Error(
'The `vitest` package was not found. Please install the package and rerun the test command.',
+ { cause: error },
);
}
const { startVitest } = vitestNodeModule;
@@ -198,6 +271,9 @@ export class VitestExecutor implements TestExecutor {
browserViewport,
);
if (browserOptions.errors?.length) {
+ this.debugLog(DebugLogLevel.Info, 'Browser configuration errors found.', {
+ errors: browserOptions.errors,
+ });
throw new Error(browserOptions.errors.join('\n'));
}
@@ -206,7 +282,14 @@ export class VitestExecutor implements TestExecutor {
this.logger.info(message);
}
}
+ this.debugLog(DebugLogLevel.Info, 'Browser configuration complete.', {
+ config: browserOptions.browser,
+ });
+ this.debugLog(
+ DebugLogLevel.Info,
+ `Verifying build results. File count: ${this.buildResultFiles.size}.`,
+ );
assert(
this.buildResultFiles.size > 0,
'buildResult must be available before initializing vitest',
@@ -234,6 +317,10 @@ export class VitestExecutor implements TestExecutor {
? await findVitestBaseConfig([projectRoot, workspaceRoot])
: runnerConfig;
+ this.debugLog(DebugLogLevel.Info, 'External Vitest configuration path:', {
+ externalConfigPath,
+ });
+
let project = projectName;
if (debug && browserOptions.browser?.instances) {
if (browserOptions.browser.instances.length > 1) {
@@ -245,6 +332,9 @@ export class VitestExecutor implements TestExecutor {
// When running browser tests, Vitest appends the browser name to the project identifier.
// The project name must match this augmented name to ensure the correct project is targeted.
project = `${projectName} (${browserOptions.browser.instances[0].browser})`;
+ this.debugLog(DebugLogLevel.Info, 'Adjusted project name for debugging with browser:', {
+ project,
+ });
}
// Filter internal entries and setup files from the include list
@@ -255,43 +345,47 @@ export class VitestExecutor implements TestExecutor {
!internalEntries.some((internal) => entry.startsWith(internal)) && !setupFileSet.has(entry)
);
});
+ this.debugLog(DebugLogLevel.Verbose, 'Included test files (after filtering):', include);
- return startVitest(
- 'test',
- undefined,
- {
- config: externalConfigPath,
- root: workspaceRoot,
- project,
- outputFile,
- cache: cacheOptions.enabled ? undefined : false,
- testNamePattern: this.options.filter,
- watch,
- ...(typeof ui === 'boolean' ? { ui } : {}),
- ...debugOptions,
- },
- {
- // Note `.vitest` is auto appended to the path.
- cacheDir: cacheOptions.path,
- server: {
- // Disable the actual file watcher. The boolean watch option above should still
- // be enabled as it controls other internal behavior related to rerunning tests.
- watch: null,
- },
- plugins: [
- await createVitestConfigPlugin({
- browser: browserOptions.browser,
- coverage,
- projectName,
- projectSourceRoot,
- optimizeDepsInclude: this.externalMetadata.implicitBrowser,
- reporters,
- setupFiles: testSetupFiles,
- projectPlugins,
- include,
- }),
- ],
+ const vitestConfig = {
+ config: externalConfigPath,
+ root: workspaceRoot,
+ project,
+ outputFile,
+ cache: cacheOptions.enabled ? undefined : (false as const),
+ testNamePattern: this.options.filter,
+ watch,
+ ...(typeof ui === 'boolean' ? { ui } : {}),
+ ...debugOptions,
+ };
+ const vitestServerConfig = {
+ // Note `.vitest` is auto appended to the path.
+ cacheDir: cacheOptions.path,
+ server: {
+ // Disable the actual file watcher. The boolean watch option above should still
+ // be enabled as it controls other internal behavior related to rerunning tests.
+ watch: null,
},
- );
+ plugins: [
+ await createVitestConfigPlugin({
+ browser: browserOptions.browser,
+ coverage,
+ projectName,
+ projectSourceRoot,
+ optimizeDepsInclude: this.externalMetadata.implicitBrowser,
+ reporters,
+ setupFiles: testSetupFiles,
+ projectPlugins,
+ include,
+ watch,
+ }),
+ ],
+ };
+
+ this.debugLog(DebugLogLevel.Info, 'Calling startVitest with final configuration.');
+ this.debugLog(DebugLogLevel.Verbose, 'Vitest config:', vitestConfig);
+ this.debugLog(DebugLogLevel.Verbose, 'Vitest server config:', vitestServerConfig);
+
+ return startVitest('test', undefined, vitestConfig, vitestServerConfig);
}
}
diff --git a/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts b/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts
index 9abe0d7be404..d36f8a05ffa6 100644
--- a/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts
+++ b/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts
@@ -46,6 +46,7 @@ interface VitestConfigPluginOptions {
projectPlugins: Exclude;
include: string[];
optimizeDepsInclude: string[];
+ watch: boolean;
}
async function findTestEnvironment(
@@ -81,6 +82,18 @@ export async function createVitestConfigPlugin(
async config(config) {
const testConfig = config.test;
+ if (reporters !== undefined) {
+ delete testConfig?.reporters;
+ }
+
+ if (
+ options.coverage.reporters !== undefined &&
+ testConfig?.coverage &&
+ 'reporter' in testConfig.coverage
+ ) {
+ delete testConfig.coverage.reporter;
+ }
+
if (testConfig?.projects?.length) {
this.warn(
'The "test.projects" option in the Vitest configuration file is not supported. ' +
@@ -97,6 +110,22 @@ export async function createVitestConfigPlugin(
delete testConfig.include;
}
+ if (testConfig?.watch !== undefined && testConfig.watch !== options.watch) {
+ this.warn(
+ `The "test.watch" option in the Vitest configuration file is overridden by the builder's ` +
+ `watch option. Please use the Angular CLI "--watch" option to enable or disable watch mode.`,
+ );
+ delete testConfig.watch;
+ }
+
+ if (testConfig?.exclude) {
+ this.warn(
+ 'The "test.exclude" option in the Vitest configuration file is evaluated after ' +
+ 'tests are compiled. For better build performance, please use the Angular CLI ' +
+ '"exclude" option instead.',
+ );
+ }
+
// Merge user-defined plugins from the Vitest config with the CLI's internal plugins.
if (config.plugins) {
const userPlugins = config.plugins.filter(
@@ -389,6 +418,9 @@ async function generateCoverageOption(
projectName: string,
): Promise {
let defaultExcludes: string[] = [];
+ // When a coverage exclude option is provided, Vitest's default coverage excludes
+ // will be overridden. To retain them, we manually fetch the defaults to append to the
+ // user's provided exclusions.
if (optionsCoverage.exclude) {
try {
const vitestConfig = await import('vitest/config');
@@ -420,12 +452,15 @@ async function generateCoverageOption(
// Special handling for `exclude`/`reporters` due to an undefined value causing upstream failures
...(optionsCoverage.exclude
? {
- exclude: [
- // Augment the default exclude https://vitest.dev/config/#coverage-exclude
- // with the user defined exclusions
- ...optionsCoverage.exclude,
- ...defaultExcludes,
- ],
+ exclude: Array.from(
+ new Set([
+ // Augment the default exclude https://vitest.dev/config/#coverage-exclude
+ // with the user defined exclusions
+ ...(configCoverage?.exclude || []),
+ ...optionsCoverage.exclude,
+ ...defaultExcludes,
+ ]),
+ ),
}
: {}),
...(optionsCoverage.reporters
diff --git a/packages/angular/build/src/builders/unit-test/schema.json b/packages/angular/build/src/builders/unit-test/schema.json
index 951fd5a29e73..46b9b5fb6276 100644
--- a/packages/angular/build/src/builders/unit-test/schema.json
+++ b/packages/angular/build/src/builders/unit-test/schema.json
@@ -75,8 +75,7 @@
},
"coverage": {
"type": "boolean",
- "description": "Enables coverage reporting for tests.",
- "default": false
+ "description": "Enables coverage reporting for tests. If not specified, the coverage configuration from a runner configuration file will be used if present. Otherwise, coverage is disabled by default."
},
"coverageInclude": {
"type": "array",
diff --git a/packages/angular/build/src/builders/unit-test/tests/behavior/coverage-ignore-comments_spec.ts b/packages/angular/build/src/builders/unit-test/tests/behavior/coverage-ignore-comments_spec.ts
new file mode 100644
index 000000000000..2c680ac08cf0
--- /dev/null
+++ b/packages/angular/build/src/builders/unit-test/tests/behavior/coverage-ignore-comments_spec.ts
@@ -0,0 +1,237 @@
+/**
+ * @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 { execute } from '../../index';
+import {
+ BASE_OPTIONS,
+ UNIT_TEST_BUILDER_INFO,
+ describeBuilder,
+ setupApplicationTarget,
+} from '../setup';
+
+describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
+ describe('Behavior: "coverage ignore comments"', () => {
+ beforeEach(async () => {
+ setupApplicationTarget(harness, { extractLicenses: false, optimization: false });
+ });
+
+ function getSpecContent(extraTest = '') {
+ return `
+import { TestBed } from '@angular/core/testing';
+import { AppComponent } from './app.component';
+
+describe('AppComponent', () => {
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [AppComponent],
+ }).compileComponents();
+ });
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(AppComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it('should render title', async () => {
+ const fixture = TestBed.createComponent(AppComponent);
+ await fixture.whenStable();
+ const compiled = fixture.nativeElement as HTMLElement;
+ expect(compiled.querySelector('h1')?.textContent).toContain('hello');
+ });
+
+ ${extraTest}
+});
+`;
+ }
+
+ async function assertNoUncoveredStatements(contextMessage: string) {
+ const { result } = await harness.executeOnce();
+ expect(result?.success).toBeTrue();
+ harness.expectFile('coverage/test/coverage-final.json').toExist();
+
+ const coverageMap = JSON.parse(harness.readFile('coverage/test/coverage-final.json'));
+ const appComponentPath = Object.keys(coverageMap).find((p) => p.includes('app.component.ts'));
+ expect(appComponentPath).toBeDefined();
+
+ const appComponentCoverage = coverageMap[appComponentPath as string];
+
+ const statementCounts = Object.values(appComponentCoverage.s);
+ const hasUncoveredStatements = statementCounts.some((count) => count === 0);
+ expect(hasUncoveredStatements).withContext(contextMessage).toBeFalse();
+ }
+
+ for (const type of ['istanbul', 'v8']) {
+ it(`should respect ${type} ignore next comments when computing coverage`, async () => {
+ harness.useTarget('test', {
+ ...BASE_OPTIONS,
+ coverage: true,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ coverageReporters: ['json'] as any,
+ });
+
+ await harness.writeFile(
+ 'src/app/app.component.ts',
+ `
+ import { Component } from '@angular/core';
+
+ @Component({
+ selector: 'app-root',
+ template: 'hello
',
+ standalone: true,
+ })
+ export class AppComponent {
+ title = 'app';
+
+ /* ${type} ignore next */
+ untestedFunction() {
+ return false;
+ }
+ }
+ `,
+ );
+
+ await harness.writeFile('src/app/app.component.spec.ts', getSpecContent());
+
+ await assertNoUncoveredStatements(
+ 'There should be no uncovered statements as the uncalled function was ignored',
+ );
+ });
+
+ it(`should respect ${type} ignore if comments when computing coverage`, async () => {
+ harness.useTarget('test', {
+ ...BASE_OPTIONS,
+ coverage: true,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ coverageReporters: ['json'] as any,
+ });
+
+ await harness.writeFile(
+ 'src/app/app.component.ts',
+ `
+ import { Component } from '@angular/core';
+
+ @Component({
+ selector: 'app-root',
+ template: 'hello
',
+ standalone: true,
+ })
+ export class AppComponent {
+ checkValue(val: boolean) {
+ /* ${type} ignore if -- @preserve */
+ if (val) {
+ return true;
+ }
+ return false;
+ }
+ }
+ `,
+ );
+
+ await harness.writeFile(
+ 'src/app/app.component.spec.ts',
+ getSpecContent(`
+ it('should exercise the function but not the if block', () => {
+ const fixture = TestBed.createComponent(AppComponent);
+ const app = fixture.componentInstance;
+ app.checkValue(false);
+ });
+ `),
+ );
+
+ await assertNoUncoveredStatements(
+ 'There should be no uncovered statements as the uncalled branch was ignored',
+ );
+ });
+
+ it(`should respect ${type} ignore else comments when computing coverage`, async () => {
+ harness.useTarget('test', {
+ ...BASE_OPTIONS,
+ coverage: true,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ coverageReporters: ['json'] as any,
+ });
+
+ await harness.writeFile(
+ 'src/app/app.component.ts',
+ `
+ import { Component } from '@angular/core';
+
+ @Component({
+ selector: 'app-root',
+ template: 'hello
',
+ standalone: true,
+ })
+ export class AppComponent {
+ checkValue(val: boolean) {
+ /* ${type} ignore else -- @preserve */
+ if (val) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+ `,
+ );
+
+ await harness.writeFile(
+ 'src/app/app.component.spec.ts',
+ getSpecContent(`
+ it('should exercise the function but not the else block', () => {
+ const fixture = TestBed.createComponent(AppComponent);
+ const app = fixture.componentInstance;
+ app.checkValue(true);
+ });
+ `),
+ );
+
+ await assertNoUncoveredStatements(
+ 'There should be no uncovered statements as the uncalled else branch was ignored',
+ );
+ });
+ }
+
+ it('should respect v8 ignore start/stop comments when computing coverage', async () => {
+ harness.useTarget('test', {
+ ...BASE_OPTIONS,
+ coverage: true,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ coverageReporters: ['json'] as any,
+ });
+
+ await harness.writeFile(
+ 'src/app/app.component.ts',
+ `
+ import { Component } from '@angular/core';
+
+ @Component({
+ selector: 'app-root',
+ template: 'hello
',
+ standalone: true,
+ })
+ export class AppComponent {
+ title = 'app';
+
+ /* v8 ignore start */
+ untestedFunction() {
+ return false;
+ }
+ /* v8 ignore stop */
+ }
+ `,
+ );
+
+ await harness.writeFile('src/app/app.component.spec.ts', getSpecContent());
+
+ await assertNoUncoveredStatements(
+ 'There should be no uncovered statements as the uncalled function was ignored',
+ );
+ });
+ });
+});
diff --git a/packages/angular/build/src/builders/unit-test/tests/behavior/runner-config-vitest_spec.ts b/packages/angular/build/src/builders/unit-test/tests/behavior/runner-config-vitest_spec.ts
index 603c69f533ea..609d736e00f7 100644
--- a/packages/angular/build/src/builders/unit-test/tests/behavior/runner-config-vitest_spec.ts
+++ b/packages/angular/build/src/builders/unit-test/tests/behavior/runner-config-vitest_spec.ts
@@ -42,6 +42,21 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
harness.expectFile('vitest-results.xml').toExist();
});
+ it('should override reporters defined in runnerConfig file when CLI option is present', async () => {
+ harness.useTarget('test', {
+ ...BASE_OPTIONS,
+ runnerConfig: 'vitest.config.ts',
+ reporters: ['default'],
+ });
+
+ harness.writeFile('vitest.config.ts', VITEST_CONFIG_CONTENT);
+
+ const { result } = await harness.executeOnce();
+ expect(result?.success).toBeTrue();
+ // The CLI option 'default' should override the 'junit' reporter in VITEST_CONFIG_CONTENT
+ harness.expectFile('vitest-results.xml').toNotExist();
+ });
+
it('should use custom reportsDirectory defined in runnerConfig file', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
@@ -92,6 +107,31 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
harness.expectFile('coverage/test/coverage-final.json').toExist();
});
+ it('should enable coverage when set in runnerConfig file without builder option', async () => {
+ harness.useTarget('test', {
+ ...BASE_OPTIONS,
+ runnerConfig: 'vitest.config.ts',
+ });
+
+ harness.writeFile(
+ 'vitest.config.ts',
+ `
+ import { defineConfig } from 'vitest/config';
+ export default defineConfig({
+ test: {
+ coverage: {
+ enabled: true,
+ },
+ },
+ });
+ `,
+ );
+
+ const { result } = await harness.executeOnce();
+ expect(result?.success).toBeTrue();
+ harness.expectFile('coverage/test/coverage-final.json').toExist();
+ });
+
it('should exclude test files based on runnerConfig file', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
@@ -139,6 +179,59 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
expect(results.numPassedTests).toBe(1);
});
+ it('should correctly merge coverage.exclude arrays from builder and runner options', async () => {
+ harness.useTarget('test', {
+ ...BASE_OPTIONS,
+ coverage: true,
+ runnerConfig: 'vitest.config.ts',
+ coverageExclude: ['src/app/cli-excluded.ts'],
+ });
+
+ harness.writeFile(
+ 'vitest.config.ts',
+ `
+ import { defineConfig } from 'vitest/config';
+ export default defineConfig({
+ test: {
+ coverage: {
+ exclude: ['src/app/config-excluded.ts'],
+ },
+ },
+ });
+ `,
+ );
+
+ // Create two files that would normally be covered
+ harness.writeFile('src/app/cli-excluded.ts', 'export const cliExcluded = true;');
+ harness.writeFile('src/app/config-excluded.ts', 'export const configExcluded = true;');
+
+ // Update the test file to import them so they're picked up by coverage
+ harness.writeFile(
+ 'src/app/app.component.spec.ts',
+ `
+ import { test, expect } from 'vitest';
+ import './cli-excluded';
+ import './config-excluded';
+ test('should pass', () => {
+ expect(true).toBe(true);
+ });
+ `,
+ );
+
+ const { result } = await harness.executeOnce();
+ expect(result?.success).toBeTrue();
+ harness.expectFile('coverage/test/coverage-final.json').toExist();
+
+ const coverageMap = JSON.parse(harness.readFile('coverage/test/coverage-final.json'));
+ const coveredFiles = Object.keys(coverageMap);
+
+ const hasCliExcluded = coveredFiles.some((f) => f.includes('cli-excluded.ts'));
+ const hasConfigExcluded = coveredFiles.some((f) => f.includes('config-excluded.ts'));
+
+ expect(hasCliExcluded).withContext('CLI target should be excluded').toBeFalse();
+ expect(hasConfigExcluded).withContext('Config file target should be excluded').toBeFalse();
+ });
+
it('should allow overriding globals to false via runnerConfig file', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
@@ -290,6 +383,71 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
// );
});
+ it('should warn and ignore "test.watch" option from runnerConfig file', async () => {
+ harness.useTarget('test', {
+ ...BASE_OPTIONS,
+ watch: false,
+ runnerConfig: 'vitest.config.ts',
+ });
+
+ harness.writeFile(
+ 'vitest.config.ts',
+ `
+ import { defineConfig } from 'vitest/config';
+ export default defineConfig({
+ test: {
+ watch: true,
+ },
+ });
+ `,
+ );
+
+ const { result, logs } = await harness.executeOnce();
+ expect(result?.success).toBeTrue();
+
+ // TODO: Re-enable once Vite logs are remapped through build system
+ // expect(logs).toContain(
+ // jasmine.objectContaining({
+ // level: 'warn',
+ // message: jasmine.stringMatching(
+ // 'The "test.watch" option in the Vitest configuration file is overridden by the builder\\'s watch option.',
+ // ),
+ // }),
+ // );
+ });
+
+ it('should warn about performance when "test.exclude" option is in runnerConfig file', async () => {
+ harness.useTarget('test', {
+ ...BASE_OPTIONS,
+ runnerConfig: 'vitest.config.ts',
+ });
+
+ harness.writeFile(
+ 'vitest.config.ts',
+ `
+ import { defineConfig } from 'vitest/config';
+ export default defineConfig({
+ test: {
+ exclude: ['src/app/non-existent.spec.ts'],
+ },
+ });
+ `,
+ );
+
+ const { result, logs } = await harness.executeOnce();
+ expect(result?.success).toBeTrue();
+
+ // TODO: Re-enable once Vite logs are remapped through build system
+ // expect(logs).toContain(
+ // jasmine.objectContaining({
+ // level: 'warn',
+ // message: jasmine.stringMatching(
+ // 'The "test.exclude" option in the Vitest configuration file is evaluated after',
+ // ),
+ // }),
+ // );
+ });
+
it(`should append "test.setupFiles" (string) from runnerConfig to the CLI's setup`, async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
diff --git a/packages/angular/build/src/builders/unit-test/tests/behavior/vitest-mock-unsupported_spec.ts b/packages/angular/build/src/builders/unit-test/tests/behavior/vitest-mock-unsupported_spec.ts
index 30565429f2ca..547d6528d86d 100644
--- a/packages/angular/build/src/builders/unit-test/tests/behavior/vitest-mock-unsupported_spec.ts
+++ b/packages/angular/build/src/builders/unit-test/tests/behavior/vitest-mock-unsupported_spec.ts
@@ -34,8 +34,35 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
`,
);
- const { result, logs } = await harness.executeOnce();
+ const { result } = await harness.executeOnce();
expect(result?.success).toBeFalse();
});
+
+ it('should not fail when vi.mock is used with a non-relative path', async () => {
+ harness.useTarget('test', {
+ ...BASE_OPTIONS,
+ });
+
+ harness.writeFile(
+ 'src/app/mock-non-relative.spec.ts',
+ `
+ import { vi } from 'vitest';
+ vi.mock('@angular/cdk', () => ({}));
+ describe('Ignored', () => { it('pass', () => expect(true).toBe(true)); });
+ `,
+ );
+
+ // Overwrite default to avoid noise
+ harness.writeFile(
+ 'src/app/app.component.spec.ts',
+ `
+ import { describe, it, expect } from 'vitest';
+ describe('Ignored', () => { it('pass', () => expect(true).toBe(true)); });
+ `,
+ );
+
+ const { result } = await harness.executeOnce();
+ expect(result?.success).toBeTrue();
+ });
});
});
diff --git a/packages/angular/build/src/builders/unit-test/tests/behavior/vitest-zone-init_spec.ts b/packages/angular/build/src/builders/unit-test/tests/behavior/vitest-zone-init_spec.ts
new file mode 100644
index 000000000000..812dba7fa70d
--- /dev/null
+++ b/packages/angular/build/src/builders/unit-test/tests/behavior/vitest-zone-init_spec.ts
@@ -0,0 +1,71 @@
+import { execute } from '../../index';
+import {
+ BASE_OPTIONS,
+ describeBuilder,
+ UNIT_TEST_BUILDER_INFO,
+ setupApplicationTarget,
+} from '../setup';
+
+describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
+ describe('Behavior: "Vitest Zone initialization"', () => {
+ // Zone.js does not current provide fakAsync support for Vitest
+ xit('should load Zone and Zone testing support by default', async () => {
+ setupApplicationTarget(harness); // Defaults include zone.js
+
+ harness.useTarget('test', {
+ ...BASE_OPTIONS,
+ });
+
+ harness.writeFile(
+ 'src/app/app.component.spec.ts',
+ `
+ import { describe, it, expect } from 'vitest';
+ import { fakeAsync, tick } from '@angular/core/testing';
+
+ describe('Zone Test', () => {
+ it('should have Zone defined', () => {
+ expect((globalThis as any).Zone).toBeDefined();
+ });
+
+ it('should support fakeAsync', fakeAsync(() => {
+ let val = false;
+ setTimeout(() => { val = true; }, 100);
+ tick(100);
+ expect(val).toBeTrue();
+ }));
+ });
+ `,
+ );
+
+ const { result } = await harness.executeOnce();
+ expect(result?.success).toBeTrue();
+ });
+
+ it('should NOT load Zone when zoneless (no zone.js in polyfills)', async () => {
+ // Setup application target WITHOUT zone.js in polyfills
+ setupApplicationTarget(harness, {
+ polyfills: [],
+ });
+
+ harness.useTarget('test', {
+ ...BASE_OPTIONS,
+ });
+
+ harness.writeFile(
+ 'src/app/app.component.spec.ts',
+ `
+ import { describe, it, expect } from 'vitest';
+
+ describe('Zoneless Test', () => {
+ it('should NOT have Zone defined', () => {
+ expect((globalThis as any).Zone).toBeUndefined();
+ });
+ });
+ `,
+ );
+
+ const { result } = await harness.executeOnce();
+ expect(result?.success).toBe(true);
+ });
+ });
+});
diff --git a/packages/angular/build/src/builders/unit-test/tests/options/code-coverage_spec.ts b/packages/angular/build/src/builders/unit-test/tests/options/code-coverage_spec.ts
index f8a8acb60591..d0f51e731de9 100644
--- a/packages/angular/build/src/builders/unit-test/tests/options/code-coverage_spec.ts
+++ b/packages/angular/build/src/builders/unit-test/tests/options/code-coverage_spec.ts
@@ -42,7 +42,7 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
harness.expectFile('coverage/test/index.html').toExist();
});
- it('should generate a code coverage report when coverage is true', async () => {
+ it('should generate a code coverage report when coverage is true and coverageReporters is set to json', async () => {
harness.useTarget('test', {
...BASE_OPTIONS,
coverage: true,
diff --git a/packages/angular/build/src/builders/unit-test/tests/options/setup-files_spec.ts b/packages/angular/build/src/builders/unit-test/tests/options/setup-files_spec.ts
index be70c833c9a9..65d0dadf17b8 100644
--- a/packages/angular/build/src/builders/unit-test/tests/options/setup-files_spec.ts
+++ b/packages/angular/build/src/builders/unit-test/tests/options/setup-files_spec.ts
@@ -36,12 +36,12 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => {
it('should include the setup files', async () => {
await harness.writeFiles({
- 'src/setup.ts': `globalThis['TEST_SETUP_RAN'] = true;`,
+ 'src/setup.ts': `(globalThis as any)['TEST_SETUP_RAN'] = true;`,
'src/app/app.component.spec.ts': `
import { describe, expect, test } from 'vitest'
describe('AppComponent', () => {
test('should have run setup file', () => {
- expect(globalThis['TEST_SETUP_RAN']).toBe(true);
+ expect((globalThis as any)['TEST_SETUP_RAN']).toBe(true);
});
});`,
});
diff --git a/packages/angular/build/src/private.ts b/packages/angular/build/src/private.ts
index 4791a94a42d3..50d57d0e5a01 100644
--- a/packages/angular/build/src/private.ts
+++ b/packages/angular/build/src/private.ts
@@ -26,6 +26,10 @@ export { buildApplicationInternal } from './builders/application';
export type { ApplicationBuilderInternalOptions } from './builders/application/options';
export { type Result, type ResultFile, ResultKind } from './builders/application/results';
export { serveWithVite } from './builders/dev-server/vite';
+export {
+ normalizeOptions as normalizeDevServerOptions,
+ type NormalizedDevServerOptions,
+} from './builders/dev-server/options';
// Tools
export * from './tools/babel/plugins';
diff --git a/packages/angular/build/src/tools/angular/angular-host.ts b/packages/angular/build/src/tools/angular/angular-host.ts
index e98ebf49f3eb..874d66fe2b41 100644
--- a/packages/angular/build/src/tools/angular/angular-host.ts
+++ b/packages/angular/build/src/tools/angular/angular-host.ts
@@ -212,6 +212,12 @@ export function createAngularCompilerHost(
return null;
}
+ // Reject TypeScript files used as component resources (e.g., styleUrl pointing to a .ts file).
+ // Processing a TypeScript file as a stylesheet or template causes confusing downstream errors.
+ if (hasTypeScriptExtension(resolvedPath)) {
+ return null;
+ }
+
// All resource names that have template file extensions are assumed to be templates
// TODO: Update compiler to provide the resource type to avoid extension matching here.
if (!hostOptions.externalStylesheets || hasTemplateExtension(resolvedPath)) {
@@ -255,15 +261,13 @@ export function createAngularCompilerHost(
return host;
}
-function hasTemplateExtension(file: string): boolean {
- const extension = nodePath.extname(file).toLowerCase();
+const TEMPLATE_EXTENSIONS: ReadonlySet = new Set(['.htm', '.html', '.svg']);
+const TYPESCRIPT_EXTENSIONS: ReadonlySet = new Set(['.ts', '.tsx', '.mts', '.cts']);
- switch (extension) {
- case '.htm':
- case '.html':
- case '.svg':
- return true;
- }
+function hasTemplateExtension(file: string): boolean {
+ return TEMPLATE_EXTENSIONS.has(nodePath.extname(file).toLowerCase());
+}
- return false;
+function hasTypeScriptExtension(file: string): boolean {
+ return TYPESCRIPT_EXTENSIONS.has(nodePath.extname(file).toLowerCase());
}
diff --git a/packages/angular/build/src/tools/babel/plugins/add-code-coverage.ts b/packages/angular/build/src/tools/babel/plugins/add-code-coverage.ts
index efa95870f698..7917c412b15d 100644
--- a/packages/angular/build/src/tools/babel/plugins/add-code-coverage.ts
+++ b/packages/angular/build/src/tools/babel/plugins/add-code-coverage.ts
@@ -7,7 +7,7 @@
*/
import { NodePath, PluginObj, types } from '@babel/core';
-import { Visitor, programVisitor } from 'istanbul-lib-instrument';
+import type { Visitor } from 'istanbul-lib-instrument';
import assert from 'node:assert';
/**
@@ -15,7 +15,9 @@ import assert from 'node:assert';
*
* @returns A babel plugin object instance.
*/
-export default function (): PluginObj {
+export default function (
+ programVisitor: typeof import('istanbul-lib-instrument').programVisitor,
+): PluginObj {
const visitors = new WeakMap();
return {
diff --git a/packages/angular/build/src/tools/babel/plugins/elide-angular-metadata_spec.ts b/packages/angular/build/src/tools/babel/plugins/elide-angular-metadata_spec.ts
index 4705d207a63a..896e1e270af4 100644
--- a/packages/angular/build/src/tools/babel/plugins/elide-angular-metadata_spec.ts
+++ b/packages/angular/build/src/tools/babel/plugins/elide-angular-metadata_spec.ts
@@ -188,7 +188,7 @@ describe('elide-angular-metadata Babel plugin', () => {
);
it(
- 'elides arrow-function-based ɵsetClassMetadataAsync',
+ 'elides ɵsetClassDebugInfo',
testCase({
input: `
import { Component } from '@angular/core';
diff --git a/packages/angular/build/src/tools/babel/plugins/pure-toplevel-functions_spec.ts b/packages/angular/build/src/tools/babel/plugins/pure-toplevel-functions_spec.ts
index 0966a67d068a..77eb42dc9bbe 100644
--- a/packages/angular/build/src/tools/babel/plugins/pure-toplevel-functions_spec.ts
+++ b/packages/angular/build/src/tools/babel/plugins/pure-toplevel-functions_spec.ts
@@ -100,7 +100,7 @@ describe('pure-toplevel-functions Babel plugin', () => {
);
it(
- 'does not annotate call expressions inside function expressions',
+ 'does not annotate call expressions inside arrow functions',
testCaseNoChange('const foo = () => { const result = someFunction(); }'),
);
@@ -115,7 +115,7 @@ describe('pure-toplevel-functions Babel plugin', () => {
);
it(
- 'does not annotate new expressions inside function expressions',
+ 'does not annotate new expressions inside arrow functions',
testCaseNoChange('const foo = () => { const result = new SomeClass(); }'),
);
diff --git a/packages/angular/build/src/tools/esbuild/angular/compiler-plugin.ts b/packages/angular/build/src/tools/esbuild/angular/compiler-plugin.ts
index af4dcaea01fb..1bcb8c40500a 100644
--- a/packages/angular/build/src/tools/esbuild/angular/compiler-plugin.ts
+++ b/packages/angular/build/src/tools/esbuild/angular/compiler-plugin.ts
@@ -220,10 +220,19 @@ export function createCompilerPlugin(
if (stylesheetResult.errors) {
(result.errors ??= []).push(...stylesheetResult.errors);
+ const { referencedFiles } = stylesheetResult;
+ if (referencedFiles) {
+ referencedFileTracker.add(containingFile, referencedFiles);
+ if (stylesheetFile) {
+ referencedFileTracker.add(stylesheetFile, referencedFiles);
+ }
+ }
+
return '';
}
const { contents, outputFiles, metafile, referencedFiles } = stylesheetResult;
+
additionalResults.set(resultSource, {
outputFiles,
metafile,
diff --git a/packages/angular/build/src/tools/esbuild/application-code-bundle.ts b/packages/angular/build/src/tools/esbuild/application-code-bundle.ts
index 635faca8c82e..d11e1b6fb63c 100644
--- a/packages/angular/build/src/tools/esbuild/application-code-bundle.ts
+++ b/packages/angular/build/src/tools/esbuild/application-code-bundle.ts
@@ -6,12 +6,12 @@
* found in the LICENSE file at https://angular.dev/license
*/
-import type { BuildOptions, PartialMessage, Plugin } from 'esbuild';
+import type { BuildOptions, Plugin } from 'esbuild';
import assert from 'node:assert';
import { createHash } from 'node:crypto';
import { extname, relative } from 'node:path';
import type { NormalizedApplicationBuildOptions } from '../../builders/application/options';
-import { ExperimentalPlatform } from '../../builders/application/schema';
+import { Platform } from '../../builders/application/schema';
import { allowMangle } from '../../utils/environment-options';
import { toPosixPath } from '../../utils/path';
import {
@@ -158,7 +158,7 @@ export function createServerPolyfillBundleOptions(
): BundlerOptionsFactory | undefined {
const serverPolyfills: string[] = [];
const polyfillsFromConfig = new Set(options.polyfills);
- const isNodePlatform = options.ssrOptions?.platform !== ExperimentalPlatform.Neutral;
+ const isNodePlatform = options.ssrOptions?.platform !== Platform.Neutral;
if (!isZonelessApp(options.polyfills)) {
serverPolyfills.push(isNodePlatform ? 'zone.js/node' : 'zone.js');
@@ -196,9 +196,14 @@ export function createServerPolyfillBundleOptions(
if (isNodePlatform) {
// Note: Needed as esbuild does not provide require shims / proxy from ESModules.
// See: https://github.com/evanw/esbuild/issues/1921.
+ // Use an alias to avoid colliding with any `createRequire` import that may
+ // already exist in the bundled user code. ESBuild processes banner content
+ // as raw text outside of its module graph, so it cannot deduplicate or
+ // rename banner imports the way it does for user imports. Without the alias,
+ // a duplicate `import { createRequire }` binding would cause a runtime error.
jsBanner.push(
- `import { createRequire } from 'node:module';`,
- `globalThis['require'] ??= createRequire(import.meta.url);`,
+ `import { createRequire as __ngCreateRequire } from 'node:module';`,
+ `globalThis['require'] ??= __ngCreateRequire(import.meta.url);`,
);
}
@@ -295,7 +300,7 @@ export function createServerMainCodeBundleOptions(
// Mark manifest and polyfills file as external as these are generated by a different bundle step.
(buildOptions.external ??= []).push(...SERVER_GENERATED_EXTERNALS);
- const isNodePlatform = options.ssrOptions?.platform !== ExperimentalPlatform.Neutral;
+ const isNodePlatform = options.ssrOptions?.platform !== Platform.Neutral;
if (!isNodePlatform) {
// `@angular/platform-server` lazily depends on `xhr2` for XHR usage with the HTTP client.
@@ -387,7 +392,7 @@ export function createSsrEntryCodeBundleOptions(
const pluginOptions = createCompilerPluginOptions(options, sourceFileCache, loadResultCache);
const ssrEntryNamespace = 'angular:ssr-entry';
const ssrInjectManifestNamespace = 'angular:ssr-entry-inject-manifest';
- const isNodePlatform = options.ssrOptions?.platform !== ExperimentalPlatform.Neutral;
+ const isNodePlatform = options.ssrOptions?.platform !== Platform.Neutral;
const jsBanner: string[] = [];
if (options.externalDependencies?.length) {
@@ -397,9 +402,14 @@ export function createSsrEntryCodeBundleOptions(
if (isNodePlatform) {
// Note: Needed as esbuild does not provide require shims / proxy from ESModules.
// See: https://github.com/evanw/esbuild/issues/1921.
+ // Use an alias to avoid colliding with any `createRequire` import that may
+ // already exist in the bundled user code. ESBuild processes banner content
+ // as raw text outside of its module graph, so it cannot deduplicate or
+ // rename banner imports the way it does for user imports. Without the alias,
+ // a duplicate `import { createRequire }` binding would cause a runtime error.
jsBanner.push(
- `import { createRequire } from 'node:module';`,
- `globalThis['require'] ??= createRequire(import.meta.url);`,
+ `import { createRequire as __ngCreateRequire } from 'node:module';`,
+ `globalThis['require'] ??= __ngCreateRequire(import.meta.url);`,
);
}
@@ -505,7 +515,7 @@ export function createSsrEntryCodeBundleOptions(
}
function getEsBuildServerCommonOptions(options: NormalizedApplicationBuildOptions): BuildOptions {
- const isNodePlatform = options.ssrOptions?.platform !== ExperimentalPlatform.Neutral;
+ const isNodePlatform = options.ssrOptions?.platform !== Platform.Neutral;
const commonOptions = getEsBuildCommonOptions(options);
commonOptions.define ??= {};
@@ -616,7 +626,11 @@ function getEsBuildCommonOptions(options: NormalizedApplicationBuildOptions): Bu
conditions,
resolveExtensions: ['.ts', '.tsx', '.mjs', '.js', '.cjs'],
metafile: true,
- legalComments: options.extractLicenses ? 'none' : 'eof',
+ legalComments: options.extractLicenses
+ ? 'none'
+ : optimizationOptions.scripts
+ ? 'eof'
+ : 'inline',
logLevel: options.verbose && !jsonLogs ? 'debug' : 'silent',
minifyIdentifiers: optimizationOptions.scripts && allowMangle,
minifySyntax: optimizationOptions.scripts,
@@ -656,12 +670,17 @@ function getEsBuildCommonPolyfillsOptions(
): BuildOptions | undefined {
const { jit, workspaceRoot, i18nOptions, externalPackages } = options;
- const buildOptions = getEsBuildCommonOptions(options);
+ let polyfills = options.polyfills ? [...options.polyfills] : [];
+
+ const buildOptions = getEsBuildCommonOptions({
+ ...options,
+ // If any polyfills are local files, disable external packages for the polyfills build.
+ // This ensures that local files are properly bundled.
+ externalPackages: polyfills.some(isLocalFile) ? false : externalPackages,
+ });
buildOptions.splitting = false;
buildOptions.plugins ??= [];
- let polyfills = options.polyfills ? [...options.polyfills] : [];
-
// Angular JIT mode requires the runtime compiler
if (jit) {
polyfills.unshift('@angular/compiler');
@@ -671,10 +690,8 @@ function getEsBuildCommonPolyfillsOptions(
// Locale data should go first so that project provided polyfill code can augment if needed.
let needLocaleDataPlugin = false;
if (i18nOptions.shouldInline) {
- if (!externalPackages) {
- // Remove localize polyfill when i18n inline transformation have been applied to all the packages.
- polyfills = polyfills.filter((path) => !path.startsWith('@angular/localize'));
- }
+ // Remove localize polyfill when i18n inline transformation have been applied to all the packages.
+ polyfills = polyfills.filter((path) => !path.startsWith('@angular/localize'));
// Add locale data for all active locales
// TODO: Inject each individually within the inlining process itself
@@ -701,7 +718,6 @@ function getEsBuildCommonPolyfillsOptions(
cache: loadResultCache,
loadContent: async (_, build) => {
let polyfillPaths = polyfills;
- let warnings: PartialMessage[] | undefined;
if (tryToResolvePolyfillsAsRelative) {
polyfillPaths = await Promise.all(
@@ -736,7 +752,6 @@ function getEsBuildCommonPolyfillsOptions(
return {
contents,
loader: 'js',
- warnings,
resolveDir: workspaceRoot,
};
},
@@ -749,3 +764,18 @@ function getEsBuildCommonPolyfillsOptions(
function entryFileToWorkspaceRelative(workspaceRoot: string, entryFile: string): string {
return './' + toPosixPath(relative(workspaceRoot, entryFile).replace(/.[mc]?ts$/, ''));
}
+
+/**
+ * Determines if a polyfill path is a local file.
+ * A local file is defined as a path starting with a `.` or having a TypeScript/JavaScript extension.
+ * `zone.js` and its subpaths are specifically excluded and treated as packages.
+ * @param path The polyfill path to check.
+ * @returns true if the path is a local file; false otherwise.
+ */
+function isLocalFile(path: string): boolean {
+ if (path.startsWith('zone.js')) {
+ return false;
+ }
+
+ return path[0] === '.' || /\.[mc]?[jt]sx?$/.test(path);
+}
diff --git a/packages/angular/build/src/tools/esbuild/i18n-inliner-worker.ts b/packages/angular/build/src/tools/esbuild/i18n-inliner-worker.ts
index 74550e83e5de..f988a4b2f0d8 100644
--- a/packages/angular/build/src/tools/esbuild/i18n-inliner-worker.ts
+++ b/packages/angular/build/src/tools/esbuild/i18n-inliner-worker.ts
@@ -196,7 +196,9 @@ async function transformWithBabel(
// Which makes it hard to find the actual error message.
const index = error.message.indexOf(')\n');
const msg = index !== -1 ? error.message.slice(0, index + 1) : error.message;
- throw new Error(`${msg}\nAn error occurred inlining file "${options.filename}"`);
+ throw new Error(`${msg}\nAn error occurred inlining file "${options.filename}"`, {
+ cause: error,
+ });
}
if (!ast) {
diff --git a/packages/angular/build/src/tools/esbuild/javascript-transformer-worker.ts b/packages/angular/build/src/tools/esbuild/javascript-transformer-worker.ts
index 7d86009b773f..dfb7cfb2087f 100644
--- a/packages/angular/build/src/tools/esbuild/javascript-transformer-worker.ts
+++ b/packages/angular/build/src/tools/esbuild/javascript-transformer-worker.ts
@@ -8,6 +8,7 @@
import { type PluginItem, transformAsync } from '@babel/core';
import fs from 'node:fs';
+import { createRequire } from 'node:module';
import path from 'node:path';
import Piscina from 'piscina';
@@ -66,8 +67,31 @@ async function transformWithBabel(
const plugins: PluginItem[] = [];
if (options.instrumentForCoverage) {
- const { default: coveragePlugin } = await import('../babel/plugins/add-code-coverage.js');
- plugins.push(coveragePlugin);
+ try {
+ let resolvedPath = 'istanbul-lib-instrument';
+ try {
+ const requireFn = createRequire(filename);
+ resolvedPath = requireFn.resolve('istanbul-lib-instrument');
+ } catch {
+ // Fallback to pool worker import traversal
+ }
+
+ const istanbul = await import(resolvedPath);
+ const programVisitor = istanbul.programVisitor ?? istanbul.default?.programVisitor;
+
+ if (!programVisitor) {
+ throw new Error('programVisitor is not available in istanbul-lib-instrument.');
+ }
+
+ const { default: coveragePluginFactory } =
+ await import('../babel/plugins/add-code-coverage.js');
+ plugins.push(coveragePluginFactory(programVisitor));
+ } catch (error) {
+ throw new Error(
+ `The 'istanbul-lib-instrument' package is required for code coverage but was not found. Please install the package.`,
+ { cause: error },
+ );
+ }
}
if (shouldLink) {
diff --git a/packages/angular/build/src/tools/vite/middlewares/assets-middleware.ts b/packages/angular/build/src/tools/vite/middlewares/assets-middleware.ts
index f0a137f578f8..e0074625afe0 100644
--- a/packages/angular/build/src/tools/vite/middlewares/assets-middleware.ts
+++ b/packages/angular/build/src/tools/vite/middlewares/assets-middleware.ts
@@ -45,8 +45,8 @@ export function createAngularAssetsMiddleware(
// Rewrite all build assets to a vite raw fs URL
const asset = assets.get(pathname);
if (asset) {
- // This is a workaround to serve CSS, JS and TS files without Vite transformations.
- if (JS_TS_REGEXP.test(extension) || CSS_PREPROCESSOR_REGEXP.test(extension)) {
+ // This is a workaround to serve extensionless, CSS, JS and TS files without Vite transformations.
+ if (!extension || JS_TS_REGEXP.test(extension) || CSS_PREPROCESSOR_REGEXP.test(extension)) {
const contents = readFileSync(asset.source);
const etag = `W/${createHash('sha256').update(contents).digest('hex')}`;
if (checkAndHandleEtag(req, res, etag)) {
diff --git a/packages/angular/build/src/tools/vite/middlewares/ssr-middleware.ts b/packages/angular/build/src/tools/vite/middlewares/ssr-middleware.ts
index 4b0a8d8390f1..a26fa8e5e257 100644
--- a/packages/angular/build/src/tools/vite/middlewares/ssr-middleware.ts
+++ b/packages/angular/build/src/tools/vite/middlewares/ssr-middleware.ts
@@ -90,6 +90,10 @@ export async function createAngularSsrExternalMiddleware(
'@angular/ssr/node' as string
)) as typeof import('@angular/ssr/node', { with: { 'resolution-mode': 'import' } });
+ // Disable host check if allowed hosts is true meaning allow all hosts.
+ const { allowedHosts } = server.config.server;
+ const disableAllowedHostsCheck = allowedHosts === true;
+
return function angularSsrExternalMiddleware(
req: Connect.IncomingMessage,
res: ServerResponse,
@@ -123,6 +127,7 @@ export async function createAngularSsrExternalMiddleware(
}
if (cachedAngularAppEngine !== AngularAppEngine) {
+ AngularAppEngine.ɵdisableAllowedHostsCheck = disableAllowedHostsCheck;
AngularAppEngine.ɵallowStaticRouteRender = true;
AngularAppEngine.ɵhooks.on('html:transform:pre', async ({ html, url }) => {
const processedHtml = await server.transformIndexHtml(url.pathname, html);
diff --git a/packages/angular/build/src/tools/vite/plugins/ssr-ssl-plugin.ts b/packages/angular/build/src/tools/vite/plugins/ssr-ssl-plugin.ts
index 80ddf56e739a..a32c87a604de 100644
--- a/packages/angular/build/src/tools/vite/plugins/ssr-ssl-plugin.ts
+++ b/packages/angular/build/src/tools/vite/plugins/ssr-ssl-plugin.ts
@@ -6,8 +6,7 @@
* found in the LICENSE file at https://angular.dev/license
*/
-import { readFile } from 'node:fs/promises';
-import { getCACertificates, rootCertificates, setDefaultCACertificates } from 'node:tls';
+import { getCACertificates, setDefaultCACertificates } from 'node:tls';
import type { Plugin } from 'vite';
export function createAngularServerSideSSLPlugin(): Plugin {
@@ -39,34 +38,8 @@ export function createAngularServerSideSSLPlugin(): Plugin {
const { cert } = https;
const additionalCerts = Array.isArray(cert) ? cert : [cert];
- // TODO(alanagius): Remove the `if` check once we only support Node.js 22.18.0+ and 24.5.0+.
- if (getCACertificates && setDefaultCACertificates) {
- const currentCerts = getCACertificates('default');
- setDefaultCACertificates([...currentCerts, ...additionalCerts]);
-
- return;
- }
-
- // TODO(alanagius): Remove the below and `undici` dependency once we only support Node.js 22.18.0+ and 24.5.0+.
- const { getGlobalDispatcher, setGlobalDispatcher, Agent } = await import('undici');
- const originalDispatcher = getGlobalDispatcher();
- const ca = [...rootCertificates, ...additionalCerts];
- const extraNodeCerts = process.env['NODE_EXTRA_CA_CERTS'];
- if (extraNodeCerts) {
- ca.push(await readFile(extraNodeCerts));
- }
-
- setGlobalDispatcher(
- new Agent({
- connect: {
- ca,
- },
- }),
- );
-
- httpServer?.on('close', () => {
- setGlobalDispatcher(originalDispatcher);
- });
+ const currentCerts = getCACertificates('default');
+ setDefaultCACertificates([...currentCerts, ...additionalCerts]);
},
};
}
diff --git a/packages/angular/build/src/utils/index-file/auto-csp.ts b/packages/angular/build/src/utils/index-file/auto-csp.ts
index c50e0bfce3f2..0e1dfe3ed916 100644
--- a/packages/angular/build/src/utils/index-file/auto-csp.ts
+++ b/packages/angular/build/src/utils/index-file/auto-csp.ts
@@ -52,12 +52,7 @@ function isJavascriptMimeType(mimeType: string): boolean {
* @returns whether to add the script tag to the dynamically loaded script tag
*/
function shouldDynamicallyLoadScriptTagBasedOnType(scriptType: string | undefined): boolean {
- return (
- scriptType === undefined ||
- scriptType === '' ||
- scriptType === 'module' ||
- isJavascriptMimeType(scriptType)
- );
+ return !scriptType || scriptType === 'module' || isJavascriptMimeType(scriptType);
}
/**
@@ -67,7 +62,11 @@ function shouldDynamicallyLoadScriptTagBasedOnType(scriptType: string | undefine
* @returns The hash of the text formatted appropriately for CSP.
*/
export function hashTextContent(scriptText: string): string {
- const hash = crypto.createHash(HASH_FUNCTION).update(scriptText, 'utf-8').digest('base64');
+ // Normalize CRLF to LF to ensure consistent since the rewriter might normalize the line endings.
+ const hash = crypto
+ .createHash(HASH_FUNCTION)
+ .update(scriptText.replace(/\r\n?/g, '\n'), 'utf-8')
+ .digest('base64');
return `'${HASH_FUNCTION}-${hash}'`;
}
diff --git a/packages/angular/build/src/utils/index-file/auto-csp_spec.ts b/packages/angular/build/src/utils/index-file/auto-csp_spec.ts
index 1ec5f2ab06aa..efbd338233ad 100644
--- a/packages/angular/build/src/utils/index-file/auto-csp_spec.ts
+++ b/packages/angular/build/src/utils/index-file/auto-csp_spec.ts
@@ -15,13 +15,13 @@ const getCsps = (html: string) => {
).map((m) => m[1]); // Only capture group.
};
-const ONE_HASH_CSP =
+const CSP_SINGLE_HASH_REGEX =
/script-src 'strict-dynamic' 'sha256-[^']+' https: 'unsafe-inline';object-src 'none';base-uri 'self';/;
-const TWO_HASH_CSP =
+const CSP_TWO_HASHES_REGEX =
/script-src 'strict-dynamic' (?:'sha256-[^']+' ){2}https: 'unsafe-inline';object-src 'none';base-uri 'self';/;
-const FOUR_HASH_CSP =
+const CSP_FOUR_HASHES_REGEX =
/script-src 'strict-dynamic' (?:'sha256-[^']+' ){4}https: 'unsafe-inline';object-src 'none';base-uri 'self';/;
describe('auto-csp', () => {
@@ -38,8 +38,8 @@ describe('auto-csp', () => {
`);
const csps = getCsps(result);
- expect(csps.length).toBe(1);
- expect(csps[0]).toMatch(ONE_HASH_CSP);
+ expect(csps).toHaveSize(1);
+ expect(csps[0]).toMatch(CSP_SINGLE_HASH_REGEX);
expect(csps[0]).toContain(hashTextContent("console.log('foo');"));
});
@@ -56,8 +56,8 @@ describe('auto-csp', () => {
`);
const csps = getCsps(result);
- expect(csps.length).toBe(1);
- expect(csps[0]).toMatch(ONE_HASH_CSP);
+ expect(csps).toHaveSize(1);
+ expect(csps[0]).toMatch(CSP_SINGLE_HASH_REGEX);
expect(result).toContain(`var scripts = [['./main.js', '', false, false]];`);
});
@@ -74,8 +74,8 @@ describe('auto-csp', () => {
`);
const csps = getCsps(result);
- expect(csps.length).toBe(1);
- expect(csps[0]).toMatch(ONE_HASH_CSP);
+ expect(csps).toHaveSize(1);
+ expect(csps[0]).toMatch(CSP_SINGLE_HASH_REGEX);
// Our loader script appears after the HTML text content.
expect(result).toMatch(
/Some text<\/div>\s*`);
// Only two loader scripts are created.
- expect(Array.from(result.matchAll(/`);
// Only one loader script is created.
- expect(Array.from(result.matchAll(/
+ Some text
+