mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
Compare commits
386 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 31f2030320 | |||
| c525a5a890 | |||
| f04a78a4fc | |||
| ea6c51f355 | |||
| ce6c7d94da | |||
| 1fd8d5d446 | |||
| 3ebaeccb46 | |||
| 3c14d0523c | |||
| 9f0c6bcf24 | |||
| 301155b96f | |||
| 522587d236 | |||
| be55f8d5de | |||
| 71955dcf7e | |||
| bc2b819e1d | |||
| 8d1891a532 | |||
| 0ebf1afb16 | |||
| 5d75219102 | |||
| 9515a70933 | |||
| 388d25e57a | |||
| 0f85742fe7 | |||
| 6fa4ac7681 | |||
| 2d0761062c | |||
| a67d82254f | |||
| 1d4f81c8ee | |||
| 2d215246c8 | |||
| ca104b32e9 | |||
| 64a36757e5 | |||
| 80962f69fa | |||
| 12daa0b52f | |||
| 12b95774bd | |||
| 78b4ddcfff | |||
| 2cd4fb10c0 | |||
| d09ef683b7 | |||
| 923c81c4a8 | |||
| dbb8dbc50b | |||
| e78451cf8a | |||
| b04e6b1869 | |||
| de18875a4b | |||
| 8858e3c908 | |||
| 720f0dbd13 | |||
| 172e44bdc2 | |||
| 150ed2b569 | |||
| d0c7ea8491 | |||
| 16d0d43ad4 | |||
| 5d9de59194 | |||
| e987d94276 | |||
| fe50e3647d | |||
| fcec9a2233 | |||
| 3c66209e0a | |||
| ba40153ac0 | |||
| a3094f2cb5 | |||
| ba00432068 | |||
| d40867c8d9 | |||
| 96a070e9e0 | |||
| ccdb340d0d | |||
| 205d5276b0 | |||
| efd76c13e7 | |||
| ef57d616af | |||
| 2a15d7856d | |||
| 669c7c0249 | |||
| 79a818bd50 | |||
| 8843707919 | |||
| bd2caa4eba | |||
| dcdca98f5d | |||
| 423cc17a76 | |||
| 5cc6c42acc | |||
| 97969b1c54 | |||
| 1c7106a79c | |||
| 8cd2d8bd4c | |||
| e3627d38e9 | |||
| b3434302d5 | |||
| 4aef17def4 | |||
| 4cd56c26a2 | |||
| dd6e1c3494 | |||
| f612437075 | |||
| 4bd20697fa | |||
| 83dbf0edfa | |||
| 049b67246f | |||
| 6e54bdfdcb | |||
| 0afd9c5632 | |||
| 444fd01acc | |||
| 9f64d1a1de | |||
| 27b2a4af3a | |||
| c80d9baf81 | |||
| 89e6636d18 | |||
| 7eb5f9a999 | |||
| 881fe73209 | |||
| 4a23f6e79d | |||
| 19462fc807 | |||
| 636cd0d3ae | |||
| 5fd0082ccf | |||
| 7fb5a8087e | |||
| a2ea2ec615 | |||
| cd36189ba7 | |||
| 38c9921ff3 | |||
| e0b9ecbad4 | |||
| 233094e425 | |||
| f4a1572861 | |||
| b05552fc7a | |||
| 653089d213 | |||
| 134c31bd1b | |||
| 03bf4df6e0 | |||
| 13e4255f9e | |||
| 6178d36a7d | |||
| c536035cbc | |||
| 6a616f81f3 | |||
| d1919262f3 | |||
| c8cde0f153 | |||
| 2ebdd57eae | |||
| c8d3439814 | |||
| 9def49211a | |||
| 15b6e1a8ab | |||
| 8449001f8c | |||
| f2051e7f72 | |||
| fc850a5db5 | |||
| 9de50b1e7d | |||
| 895f0fb814 | |||
| 3ec8a5c753 | |||
| 4ee399388e | |||
| 8e84429203 | |||
| 185ac24ab6 | |||
| e5de4109e6 | |||
| 00b87a7c26 | |||
| 50b3e9f4c4 | |||
| b62cadbe5d | |||
| a686e34da1 | |||
| 4c3fda7dc3 | |||
| b7bc11098d | |||
| 58ddd8bff6 | |||
| 219462fe0f | |||
| c36cd73b78 | |||
| 32ff9eb566 | |||
| e539e44c85 | |||
| 1c0cc27e6a | |||
| c7fd7f9a02 | |||
| 5f5828f60d | |||
| e00c5c5b3f | |||
| 10827a41ea | |||
| e4594ef2d8 | |||
| aaacb15bf1 | |||
| 1b7f53021f | |||
| ff6038d72d | |||
| 0495254143 | |||
| 7dde1951d0 | |||
| 8dc34965e3 | |||
| e027406026 | |||
| 7157099669 | |||
| 69b61cbc24 | |||
| 8266790fb8 | |||
| 802dbcc2a0 | |||
| 9096d45377 | |||
| 34c5a039d1 | |||
| 7a7725ee4c | |||
| 650cd6b903 | |||
| d0f0c0824f | |||
| b1aa2d016a | |||
| 68e69670be | |||
| 749a71c7e5 | |||
| 3fb7a12078 | |||
| 6117ccee2e | |||
| 90e798b675 | |||
| c7b84f5a22 | |||
| a857f72c81 | |||
| 067a3a5a76 | |||
| ad1f106f6d | |||
| 9943230f91 | |||
| 35a1d5d7bf | |||
| b3d102b475 | |||
| a6ed784508 | |||
| cf690982de | |||
| f21c7565d7 | |||
| a52f4a1987 | |||
| d124fba6ff | |||
| a6640e3cc5 | |||
| f2ec6fbfa7 | |||
| f18980c18d | |||
| c3303a5f75 | |||
| f697995c26 | |||
| e1dd3b10dd | |||
| a3f808d7c8 | |||
| 951701dabb | |||
| 08d363e2cc | |||
| 320f1d6537 | |||
| 991785fc18 | |||
| fc80684fa3 | |||
| 452abfc33c | |||
| 869312ef71 | |||
| 0c2255bde8 | |||
| d1d32db972 | |||
| f4a2d0abc5 | |||
| 7bbcb54241 | |||
| 1e47a87fc6 | |||
| dc64f3e478 | |||
| 957b36798f | |||
| bf74c56fe1 | |||
| c664af8dec | |||
| d6f7f68012 | |||
| 043017871c | |||
| 3b42314bb5 | |||
| 15fa2ce1c4 | |||
| f204b54f9a | |||
| 3bd3c902ad | |||
| dddce8311f | |||
| e9096b9aab | |||
| 6e5647f028 | |||
| 5bb4b75488 | |||
| 8dbeac1341 | |||
| ed50120d4c | |||
| 1abf6d5abd | |||
| c357a03bc6 | |||
| 6a5d12a186 | |||
| 7f5c1310f3 | |||
| 22214593ab | |||
| 5a989f26f3 | |||
| ae2a1faaad | |||
| 64ddde21af | |||
| 479a919f42 | |||
| 98468c0d7a | |||
| b549cd28ae | |||
| cef1449ac7 | |||
| 05a9322c75 | |||
| df89611bf2 | |||
| 35dea23580 | |||
| 596b545130 | |||
| 1e3999ec36 | |||
| 114906d2d6 | |||
| 2e05deaffb | |||
| 2fb029de04 | |||
| 690405b253 | |||
| fe533cd244 | |||
| 421edfe04b | |||
| 87bfe164e3 | |||
| a6a46e9839 | |||
| 9de2f078d5 | |||
| 40844876ea | |||
| ca61366218 | |||
| cd748aad47 | |||
| fce5602b53 | |||
| f87fad3fff | |||
| 06c53679ba | |||
| e8d9bc5119 | |||
| 26072bba9c | |||
| 00bfff52c2 | |||
| 0bc7f154c8 | |||
| 705b94bd21 | |||
| ed6a347109 | |||
| 6d6b0278a8 | |||
| 6e0b66d702 | |||
| 47caed9ada | |||
| 23d1ace339 | |||
| e220a615d1 | |||
| d7b6045d61 | |||
| c9b0f45690 | |||
| 15395a576f | |||
| 9a27c5befb | |||
| a26d94d979 | |||
| 328e588bcd | |||
| 454a269254 | |||
| f881082e80 | |||
| c8f07daf8f | |||
| 3f247f1256 | |||
| aabe146c30 | |||
| 650ad1f7fb | |||
| 269f344d45 | |||
| a649d85de1 | |||
| 020e0f0424 | |||
| 10b0927e15 | |||
| 28c28a0aa4 | |||
| eef93f5a7b | |||
| e1481d89dc | |||
| e8aa7d3ce3 | |||
| b02042301a | |||
| 551c99fd5f | |||
| 6c421ed65d | |||
| 9093e0e132 | |||
| 040cba0b09 | |||
| 6213c7d998 | |||
| 49e00b306a | |||
| 1c27fa982c | |||
| a28672fb70 | |||
| 65c5a734e2 | |||
| 9bdb4fe8cc | |||
| 11c638557b | |||
| a6130e076a | |||
| 906bce0877 | |||
| a827f02746 | |||
| eb0183009b | |||
| 9e172e0280 | |||
| 878b99a1c6 | |||
| 9655ea7f5c | |||
| d76a911669 | |||
| 591c7e2ec8 | |||
| 81dc006772 | |||
| c0eb0cb7a5 | |||
| 416878bb7c | |||
| fceb19128d | |||
| f9d73cc687 | |||
| 5ceb1167fb | |||
| 6b475eb86c | |||
| 9eac011bd0 | |||
| 0ba671d4a0 | |||
| 38b9b79c29 | |||
| 1edb83c019 | |||
| be0e8457b7 | |||
| aff482913b | |||
| 9374d57330 | |||
| 52106fb71b | |||
| 58765137ab | |||
| 8164b2298e | |||
| 966d95df20 | |||
| fe27810a24 | |||
| 4dfd38e127 | |||
| a8c5bafd18 | |||
| d8254d95e9 | |||
| 0c61fc6d43 | |||
| 0da66e6d43 | |||
| 7b51728813 | |||
| 5be80d33a3 | |||
| 1e3748b893 | |||
| 87523e90bc | |||
| bdc31675b7 | |||
| 726d6026b1 | |||
| 0eb550e297 | |||
| 67cfe686b1 | |||
| c7440dddef | |||
| c714426963 | |||
| a1fe7091f9 | |||
| af63b800ec | |||
| 9e828d5e54 | |||
| 7a4bc79720 | |||
| 1d5ea07bca | |||
| 2031359069 | |||
| 0838df97bf | |||
| dbd403031a | |||
| 287d6c8fdd | |||
| 732aadf082 | |||
| 2d8ac7d9ef | |||
| 28b8782266 | |||
| d8ac27f94a | |||
| 81751465b4 | |||
| de6b6413e4 | |||
| d4a380d7bd | |||
| 124d44f130 | |||
| 19ed12c81e | |||
| 557e6fe6eb | |||
| b7cefe0370 | |||
| 8b02aa0333 | |||
| c68a099ded | |||
| dc36fa5200 | |||
| e607d4db2b | |||
| e159ab1ee3 | |||
| 24e7238c54 | |||
| f863792923 | |||
| 65cca30361 | |||
| 8747f9ad55 | |||
| 3ecf8c7114 | |||
| a6a2e38e5c | |||
| 2faed63fed | |||
| 1cf4d34c37 | |||
| ced0357bf8 | |||
| d9f0e6b27c | |||
| 767a280562 | |||
| 254b560138 | |||
| b0900ccee5 | |||
| 0065ebb115 | |||
| 0211668d20 | |||
| ada3c37338 | |||
| 4635095c01 | |||
| b13c3fc2eb | |||
| c81e345e72 | |||
| 31df4d2fd1 | |||
| 0e3465dfd9 | |||
| 6fb2b71489 | |||
| 5105477490 | |||
| 856dd50e64 | |||
| 1a16fb7aa2 | |||
| 0e1bd1b404 | |||
| 4ab709460a | |||
| b38c43bacc | |||
| 88d48de46e | |||
| af247cacb6 | |||
| 499fa8fa88 | |||
| 79b91593c9 | |||
| 51801612c3 | |||
| a92dea5b99 | |||
| 1156d7d786 |
+6
-2
@@ -35,10 +35,11 @@ integration/standalone-bootstrap/node_modules
|
||||
integration/terser/node_modules
|
||||
integration/trusted-types/node_modules
|
||||
integration/typings_test_rxjs7/node_modules
|
||||
integration/typings_test_ts58/node_modules
|
||||
integration/legacy-animations/node_modules
|
||||
integration/legacy-animations-async/node_modules
|
||||
integration/typings_test_ts59/node_modules
|
||||
modules/ssr-benchmarks/node_modules
|
||||
|
||||
|
||||
# For rules_js
|
||||
adev/node_modules
|
||||
adev/shared-docs/node_modules
|
||||
@@ -61,7 +62,10 @@ packages/platform-server/node_modules
|
||||
packages/platform-browser-dynamic/node_modules
|
||||
packages/router/node_modules
|
||||
packages/zone.js/node_modules
|
||||
packages/zone.js/test/typings/node_modules
|
||||
packages/upgrade/node_modules
|
||||
packages/benchpress/node_modules
|
||||
packages/service-worker/node_modules
|
||||
packages/zone.js/test/typings/node_modules
|
||||
packages/zone.js/node_modules
|
||||
tools/bazel/rules_angular_store/node_modules
|
||||
|
||||
@@ -12,6 +12,9 @@ common --@aspect_rules_ts//ts:default_to_tsc_transpiler
|
||||
# Needed as otherwise `env` of TS actions would be ignored.
|
||||
common --incompatible_merge_fixed_and_default_shell_env
|
||||
|
||||
# Frozen lockfile
|
||||
common --lockfile_mode=error
|
||||
|
||||
###############################
|
||||
# Filesystem interactions #
|
||||
###############################
|
||||
@@ -166,8 +169,6 @@ test:saucelabs --flaky_test_attempts=1
|
||||
|
||||
# --ng_perf will ask the Ivy compiler to produce performance results for each build.
|
||||
build --flag_alias=ng_perf=//packages/compiler-cli:ng_perf
|
||||
# --full_build_adev will run adev build/serve in a full mode, performing prerendering and DCE.
|
||||
build --flag_alias=full_build_adev=//adev:full_build_adev
|
||||
|
||||
####################################################
|
||||
# User bazel configuration
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
6.5.0
|
||||
7.6.1
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
load("//tools:defaults2.bzl", "esbuild_checked_in", "ts_config", "ts_project")
|
||||
load("//tools:defaults.bzl", "esbuild_checked_in", "ts_config", "ts_project")
|
||||
|
||||
package(default_visibility = ["//.github/actions/deploy-docs-site:__subpackages__"])
|
||||
|
||||
@@ -12,6 +12,7 @@ esbuild_checked_in(
|
||||
entry_point = ":lib/main.mts",
|
||||
external = [
|
||||
"undici",
|
||||
"pnpapi",
|
||||
],
|
||||
metafile = False,
|
||||
platform = "node",
|
||||
|
||||
+15768
-3521
File diff suppressed because one or more lines are too long
@@ -5,9 +5,9 @@ runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Saucelabs Variables
|
||||
uses: angular/dev-infra/github-actions/saucelabs@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/saucelabs@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Starting Saucelabs tunnel service
|
||||
shell: bash
|
||||
run: ./tools/saucelabs/sauce-service.sh run &
|
||||
|
||||
@@ -15,22 +15,22 @@ permissions: read-all
|
||||
|
||||
jobs:
|
||||
adev-build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-latest-8core
|
||||
if: |
|
||||
(github.event.action == 'labeled' && github.event.label.name == 'adev: preview') ||
|
||||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'adev: preview'))
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Install node modules
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build adev to ensure it continues to work
|
||||
run: pnpm bazel build //adev:build --full_build_adev --config=release
|
||||
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
- name: Build adev
|
||||
run: pnpm bazel build //adev:build.production
|
||||
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
with:
|
||||
workflow-artifact-name: 'adev-preview'
|
||||
pull-number: '${{github.event.pull_request.number}}'
|
||||
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
token: '${{secrets.GITHUB_TOKEN}}'
|
||||
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
npx -y firebase-tools@latest target:clear --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs
|
||||
npx -y firebase-tools@latest target:apply --config adev/firebase.json --project ${{env.PREVIEW_PROJECT}} hosting angular-docs ${{env.PREVIEW_SITE}}
|
||||
|
||||
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
with:
|
||||
github-token: '${{secrets.GITHUB_TOKEN}}'
|
||||
workflow-artifact-name: 'adev-preview'
|
||||
|
||||
@@ -13,9 +13,9 @@ jobs:
|
||||
assistant_to_the_branch_manager:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: angular/dev-infra/github-actions/branch-manager@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
- uses: angular/dev-infra/github-actions/branch-manager@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
- uses: alessbell/pull-request-comment-branch@aad01d65d6982b8eacabed5e9a684cd8ceb98da6 # v1.1
|
||||
id: comment-branch
|
||||
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
# Specify repository as the PR branch might be from a fork.
|
||||
repository: ${{steps.comment-branch.outputs.head_owner}}/${{steps.comment-branch.outputs.head_repo}}
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
- run: pnpm install --frozen-lockfile
|
||||
|
||||
- uses: angular/dev-infra/github-actions/bazel/configure-remote@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
- uses: angular/dev-infra/github-actions/bazel/configure-remote@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
with:
|
||||
bazelrc: ./.bazelrc.user
|
||||
|
||||
|
||||
+38
-46
@@ -21,9 +21,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
with:
|
||||
cache-node-modules: true
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Install node modules
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Check code lint
|
||||
@@ -41,15 +39,20 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
with:
|
||||
cache-node-modules: true
|
||||
disable-package-manager-cache: true
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
with:
|
||||
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
|
||||
- name: Cache downloaded Cypress binary
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
with:
|
||||
path: '~/.cache/Cypress'
|
||||
key: cypress-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
- name: Install node modules
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Run unit tests
|
||||
@@ -63,18 +66,17 @@ jobs:
|
||||
start: pnpm bazel run //devtools/src:devserver
|
||||
wait-on: 'http://localhost:4200'
|
||||
wait-on-timeout: 300
|
||||
install: false
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest-4core
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
with:
|
||||
cache-node-modules: true
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel Remote Caching
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
with:
|
||||
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
|
||||
- name: Install node modules
|
||||
@@ -83,16 +85,14 @@ jobs:
|
||||
run: pnpm test:ci
|
||||
|
||||
integration-tests:
|
||||
runs-on: ubuntu-latest-4core
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
with:
|
||||
cache-node-modules: true
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel Remote Caching
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
with:
|
||||
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
|
||||
- name: Install node modules
|
||||
@@ -102,14 +102,14 @@ jobs:
|
||||
|
||||
adev:
|
||||
runs-on:
|
||||
labels: ubuntu-latest-4core
|
||||
labels: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
with:
|
||||
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
|
||||
- name: Install node modules
|
||||
@@ -117,20 +117,20 @@ jobs:
|
||||
- name: Run tests
|
||||
run: pnpm bazel test //adev/...
|
||||
- name: Build adev in fast mode to ensure it continues to work
|
||||
run: pnpm bazel build //adev:build --config=release
|
||||
run: pnpm bazel build //adev:build
|
||||
|
||||
publish-snapshots:
|
||||
runs-on:
|
||||
labels: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Install node modules
|
||||
run: pnpm install --frozen-lockfile
|
||||
- run: echo "https://${{secrets.SNAPSHOT_BUILDS_GITHUB_TOKEN}}:@github.com" > ${HOME}/.git_credentials
|
||||
@@ -139,20 +139,14 @@ jobs:
|
||||
|
||||
zone-js:
|
||||
runs-on:
|
||||
labels: ubuntu-latest-4core
|
||||
labels: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
with:
|
||||
cache-node-modules: true
|
||||
node-module-directories: |
|
||||
./node_modules
|
||||
./packages/zone.js/node_modules
|
||||
./packages/zone.js/test/typings/node_modules
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
with:
|
||||
google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }}
|
||||
- name: Install node modules
|
||||
@@ -186,14 +180,12 @@ jobs:
|
||||
- run: pnpm -C packages/zone.js/test/typings test
|
||||
|
||||
# saucelabs:
|
||||
# runs-on: ubuntu-latest-4core
|
||||
# runs-on: ubuntu-latest
|
||||
# env:
|
||||
# SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
|
||||
# steps:
|
||||
# - name: Initialize environment
|
||||
# uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b5a3609f89c06eb4037dce22a93641213a5d1508
|
||||
# with:
|
||||
# cache-node-modules: true
|
||||
# - name: Install node modules
|
||||
# run: pnpm install --frozen-lockfile
|
||||
# - uses: ./.github/actions/saucelabs-legacy
|
||||
@@ -201,18 +193,18 @@ jobs:
|
||||
adev-deploy:
|
||||
needs: [adev]
|
||||
if: needs.adev.result == 'success'
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-latest-8core
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Install node modules
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Build adev to ensure it continues to work
|
||||
run: pnpm bazel build //adev:build --full_build_adev --config=release
|
||||
- name: Build adev
|
||||
run: pnpm bazel build //adev:build.production --config=release
|
||||
- name: Deploy to firebase
|
||||
uses: ./.github/actions/deploy-docs-site
|
||||
with:
|
||||
|
||||
@@ -12,14 +12,14 @@ jobs:
|
||||
labels:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: angular/dev-infra/github-actions/pull-request-labeling@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: angular/dev-infra/github-actions/pull-request-labeling@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
post_approval_changes:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: angular/dev-infra/github-actions/post-approval-changes@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: angular/dev-infra/github-actions/post-approval-changes@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -13,8 +13,8 @@ jobs:
|
||||
statuses: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- uses: angular/dev-infra/github-actions/google-internal-tests@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- uses: angular/dev-infra/github-actions/google-internal-tests@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
with:
|
||||
run-tests-guide-url: http://go/angular-g3sync-start
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -8,22 +8,22 @@ jobs:
|
||||
# Bazel saucelabs job resides in `manual.yml` because it's currently unstable, but
|
||||
# kept as "runnable" for debugging/stabilization effort purposes.
|
||||
bazel-saucelabs:
|
||||
runs-on: ubuntu-latest-4core
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
JOBS: 2
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Install node modules
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel Remote Caching
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Saucelabs Variables
|
||||
uses: angular/dev-infra/github-actions/saucelabs@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/saucelabs@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Set up Sauce Tunnel Daemon
|
||||
run: pnpm bazel run //tools/saucelabs-daemon/background-service -- $JOBS &
|
||||
env:
|
||||
|
||||
@@ -9,6 +9,6 @@ jobs:
|
||||
status:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: angular/dev-infra/github-actions/unified-status-check@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
- uses: angular/dev-infra/github-actions/unified-status-check@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
workflows: ${{ steps.workflows.outputs.workflows }}
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Install node modules
|
||||
run: pnpm install --frozen-lockfile
|
||||
- id: workflows
|
||||
@@ -36,16 +36,16 @@ jobs:
|
||||
workflow: ${{ fromJSON(needs.list.outputs.workflows) }}
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- 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
|
||||
# identity federation. This allows us to request short lived credentials on demand, rather than storing
|
||||
# credentials in secrets long term. More information can be found at:
|
||||
# https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-google-cloud-platform
|
||||
- uses: 'google-github-actions/auth@6fc4af4b145ae7821d527454aa9bd537d1f2dc5f' # v2
|
||||
- uses: 'google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed' # v2
|
||||
with:
|
||||
project_id: 'internal-200822'
|
||||
workload_identity_provider: 'projects/823469418460/locations/global/workloadIdentityPools/measurables-tracking/providers/angular'
|
||||
|
||||
+30
-40
@@ -19,9 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
with:
|
||||
cache-node-modules: true
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Install node modules
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Check code lint
|
||||
@@ -39,7 +37,7 @@ jobs:
|
||||
- name: Check code format
|
||||
run: pnpm ng-dev format changed --check ${{ github.event.pull_request.base.sha }}
|
||||
- name: Check Package Licenses
|
||||
uses: angular/dev-infra/github-actions/linting/licenses@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/linting/licenses@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
with:
|
||||
allow-dependencies-licenses: 'pkg:npm/google-protobuf@'
|
||||
|
||||
@@ -47,13 +45,18 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
with:
|
||||
cache-node-modules: true
|
||||
disable-package-manager-cache: true
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Cache downloaded Cypress binary
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
with:
|
||||
path: '~/.cache/Cypress'
|
||||
key: cypress-cache-${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||||
- name: Install node modules
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Run unit tests
|
||||
@@ -67,18 +70,17 @@ jobs:
|
||||
start: pnpm bazel run //devtools/src:devserver
|
||||
wait-on: 'http://localhost:4200'
|
||||
wait-on-timeout: 300
|
||||
install: false
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest-8core
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
with:
|
||||
cache-node-modules: true
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel Remote Caching
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Install node modules
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Run CI tests for framework
|
||||
@@ -95,16 +97,14 @@ jobs:
|
||||
retention-days: 1
|
||||
|
||||
integration-tests:
|
||||
runs-on: ubuntu-latest-4core
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
with:
|
||||
cache-node-modules: true
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel Remote Caching
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Install node modules
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Run integration CI tests for framework
|
||||
@@ -112,37 +112,31 @@ jobs:
|
||||
|
||||
adev:
|
||||
runs-on:
|
||||
labels: ubuntu-latest-8core
|
||||
labels: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Install node modules
|
||||
run: pnpm install --frozen-lockfile
|
||||
- name: Run tests
|
||||
run: pnpm bazel test //adev/...
|
||||
- name: Build adev in fast mode to ensure it continues to work
|
||||
run: pnpm bazel build //adev:build --config=release
|
||||
run: pnpm bazel build //adev:build
|
||||
|
||||
zone-js:
|
||||
runs-on:
|
||||
labels: ubuntu-latest-4core
|
||||
labels: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
with:
|
||||
cache-node-modules: true
|
||||
node-module-directories: |
|
||||
./node_modules
|
||||
./packages/zone.js/node_modules
|
||||
./packages/zone.js/test/typings/node_modules
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@4d2f875ec29ee71e0fe1a349a99c5ab2ccb71e30
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@08d451f9b4e900928b65cd31234693ef9a994492
|
||||
- name: Install node modules
|
||||
run: pnpm install --frozen-lockfile
|
||||
- run: |
|
||||
@@ -163,8 +157,6 @@ jobs:
|
||||
cp dist/bin/packages/zone.js/npm_package/bundles/zone-patch-electron.umd.js ./packages/zone.js/test/extra/
|
||||
cp dist/bin/packages/zone.js/test/closure/zone.closure.js ./packages/zone.js/build/test/zone.closure.mjs
|
||||
|
||||
# Install
|
||||
- run: pnpm -C packages/zone.js install --frozen-lockfile
|
||||
# Run zone.js tools tests
|
||||
- run: pnpm -C packages/zone.js promisefinallytest
|
||||
- run: pnpm -C packages/zone.js jest:test
|
||||
@@ -174,14 +166,12 @@ jobs:
|
||||
- run: pnpm -C packages/zone.js/test/typings test
|
||||
|
||||
# saucelabs:
|
||||
# runs-on: ubuntu-latest-4core
|
||||
# runs-on: ubuntu-latest
|
||||
# env:
|
||||
# SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
|
||||
# steps:
|
||||
# - name: Initialize environment
|
||||
# uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@b5a3609f89c06eb4037dce22a93641213a5d1508
|
||||
# with:
|
||||
# cache-node-modules: true
|
||||
# - name: Install node modules
|
||||
# run: pnpm install --frozen-lockfile
|
||||
# - uses: ./.github/actions/saucelabs-legacy
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: 'Checkout code'
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -47,6 +47,6 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: 'Upload to code-scanning'
|
||||
uses: github/codeql-action/upload-sarif@51f77329afa6477de8c49fc9c7046c15b9a4e79d # v3.29.5
|
||||
uses: github/codeql-action/upload-sarif@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
@@ -24,7 +24,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
# Setting `persist-credentials: false` prevents the github-action account from being the
|
||||
# account that is attempted to be used for authentication, instead the remote is set to
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
env:
|
||||
ANGULAR_CDK_BUILDS_READONLY_GITHUB_TOKEN: ${{ secrets.ANGULAR_CDK_BUILDS_READONLY_GITHUB_TOKEN }}
|
||||
- name: Create a PR CDK apis (if necessary)
|
||||
uses: peter-evans/create-pull-request@v7.0.8 # v7.0.8
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||
with:
|
||||
token: ${{ secrets.ANGULAR_ROBOT_ACCESS_TOKEN }}
|
||||
push-to-fork: 'angular-robot/angular'
|
||||
@@ -52,6 +52,7 @@ jobs:
|
||||
labels: |
|
||||
action: merge
|
||||
area: docs
|
||||
target: automation
|
||||
commit-message: |
|
||||
docs: update Angular CDK apis
|
||||
|
||||
@@ -66,7 +67,7 @@ jobs:
|
||||
env:
|
||||
ANGULAR_CLI_BUILDS_READONLY_GITHUB_TOKEN: ${{ secrets.ANGULAR_CLI_BUILDS_READONLY_GITHUB_TOKEN }}
|
||||
- name: Create a PR CLI help (if necessary)
|
||||
uses: peter-evans/create-pull-request@v7.0.8 # v7.0.8
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||
with:
|
||||
token: ${{ secrets.ANGULAR_ROBOT_ACCESS_TOKEN }}
|
||||
push-to-fork: 'angular-robot/angular'
|
||||
@@ -81,6 +82,7 @@ jobs:
|
||||
labels: |
|
||||
action: merge
|
||||
area: docs
|
||||
target: automation
|
||||
commit-message: |
|
||||
docs: update Angular CLI help
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ export const format: FormatConfig = {
|
||||
// Both third_party and .yarn are directories containing copied code which should
|
||||
// not be modified.
|
||||
'!third_party/**',
|
||||
'!.yarn/**',
|
||||
// Do not format the locale files which are checked-in for Google3, but generated using
|
||||
// the `generate-locales-tool` from `packages/common/locales`.
|
||||
'!packages/core/src/i18n/locale_en.ts',
|
||||
|
||||
@@ -2,7 +2,6 @@ import {ReleaseConfig} from '@angular/ng-dev';
|
||||
|
||||
/** Configuration for the `ng-dev release` command. */
|
||||
export const release: ReleaseConfig = {
|
||||
rulesJsInteropMode: true,
|
||||
publishRegistry: 'https://wombat-dressing-room.appspot.com',
|
||||
representativeNpmPackage: '@angular/core',
|
||||
npmPackages: [
|
||||
|
||||
+24
-6
@@ -56,8 +56,8 @@
|
||||
|
||||
version: 3
|
||||
|
||||
# availability:
|
||||
# users_unavailable: ['atscott']
|
||||
#availability:
|
||||
# users_unavailable: []
|
||||
|
||||
# Meta field that goes unused by PullApprove to allow for defining aliases to be
|
||||
# used throughout the config.
|
||||
@@ -135,6 +135,9 @@ groups:
|
||||
- kirjs
|
||||
- JoostK
|
||||
- mmalerba
|
||||
- ~amishne
|
||||
- ~leonsenft
|
||||
- ~mattrbeck
|
||||
|
||||
# =========================================================
|
||||
# Framework: General (most code in our packages)
|
||||
@@ -177,6 +180,10 @@ groups:
|
||||
- thePunderWoman
|
||||
- ~pkozlowski-opensource
|
||||
- mmalerba
|
||||
- JeanMeche
|
||||
- ~amishne
|
||||
- ~leonsenft
|
||||
- ~mattrbeck
|
||||
|
||||
# =========================================================
|
||||
# Framework: Security-sensitive files which require extra review
|
||||
@@ -230,6 +237,9 @@ groups:
|
||||
- thePunderWoman
|
||||
- ~pkozlowski-opensource
|
||||
- mmalerba
|
||||
- ~amishne
|
||||
- ~leonsenft
|
||||
- ~mattrbeck
|
||||
|
||||
# =========================================================
|
||||
# Tooling: Compiler API shared with Angular CLI
|
||||
@@ -273,7 +283,7 @@ groups:
|
||||
- bencodezen
|
||||
- crisbeto
|
||||
- kirjs
|
||||
- ~JeanMeche
|
||||
- JeanMeche
|
||||
- jelbourn
|
||||
- thePunderWoman
|
||||
- devversion
|
||||
@@ -283,6 +293,9 @@ groups:
|
||||
- MarkTechson
|
||||
- mmalerba
|
||||
- ~hawkgs
|
||||
- ~amishne
|
||||
- ~leonsenft
|
||||
- ~mattrbeck
|
||||
|
||||
# =========================================================
|
||||
# Angular DevTools
|
||||
@@ -320,7 +333,6 @@ groups:
|
||||
'.gemini/**/{*,.*}',
|
||||
'.ng-dev/**/{*,.*}',
|
||||
'.vscode/**/{*,.*}',
|
||||
'.yarn/**/{*,.*}',
|
||||
'contributing-docs/*.md',
|
||||
'contributing-docs/images/**/{*,.*}',
|
||||
'goldens/{*,.*}',
|
||||
@@ -397,9 +409,12 @@ groups:
|
||||
- ~iteriani
|
||||
- ~tbondwilkinson
|
||||
- ~rahatarmanahmed
|
||||
- ~amishne
|
||||
- ~leonsenft
|
||||
- ~mattrbeck
|
||||
reviews:
|
||||
request: 3 # Request reviews from 3 people
|
||||
required: 2 # Require that 2 people approve
|
||||
request: 2
|
||||
required: 1
|
||||
reviewed_for: required
|
||||
|
||||
# ================================================
|
||||
@@ -424,6 +439,9 @@ groups:
|
||||
- thePunderWoman
|
||||
- ~pkozlowski-opensource
|
||||
- mmalerba
|
||||
- ~amishne
|
||||
- ~leonsenft
|
||||
- ~mattrbeck
|
||||
reviews:
|
||||
request: 2 # Request reviews from 2 people
|
||||
required: 1 # Require that 1 person approve
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# Yarn Vendoring
|
||||
We utilize Yarn's `yarn-path` configuration in a shared `.yarnrc` file to enforce
|
||||
everyone using the same version of Yarn. Yarn checks the `.yarnrc` file to
|
||||
determine if yarn should delegate the command to a vendored version at the
|
||||
provided path.
|
||||
|
||||
## How to update
|
||||
To update to the latest version of Yarn as our vendored version:
|
||||
- Run this command
|
||||
```sh
|
||||
yarn policies set-version latest
|
||||
```
|
||||
- Remove the previous version
|
||||
Vendored
-148049
File diff suppressed because one or more lines are too long
@@ -1,5 +0,0 @@
|
||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
yarn-path ".yarn/releases/yarn-1.22.22.cjs"
|
||||
+8
-15
@@ -1,7 +1,7 @@
|
||||
load("@aspect_rules_js//js:defs.bzl", "js_library")
|
||||
load("@npm2//:defs.bzl", "npm_link_all_packages")
|
||||
load("//:yarn.bzl", "YARN_PATH")
|
||||
load("//tools:defaults2.bzl", "copy_to_bin", "js_binary")
|
||||
load("@devinfra//bazel/validation:defs.bzl", "validate_ts_version_matching")
|
||||
load("@npm//:defs.bzl", "npm_link_all_packages")
|
||||
load("//tools:defaults.bzl", "copy_to_bin")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
@@ -9,9 +9,7 @@ exports_files([
|
||||
"LICENSE",
|
||||
"karma-js.conf.js",
|
||||
"browser-providers.conf.js",
|
||||
YARN_PATH,
|
||||
"package.json",
|
||||
"angular.json",
|
||||
])
|
||||
|
||||
npm_link_all_packages(
|
||||
@@ -25,6 +23,11 @@ filegroup(
|
||||
srcs = ["package.json"],
|
||||
)
|
||||
|
||||
validate_ts_version_matching(
|
||||
module_lock_file = "MODULE.bazel.lock",
|
||||
package_json = "package.json",
|
||||
)
|
||||
|
||||
alias(
|
||||
name = "tsconfig.json",
|
||||
actual = "//packages:tsconfig-build.json",
|
||||
@@ -60,16 +63,6 @@ config_setting(
|
||||
values = {"stamp": "true"},
|
||||
)
|
||||
|
||||
js_binary(
|
||||
name = "yarn_vendored",
|
||||
data = [".yarn/releases/yarn-1.22.22.cjs"],
|
||||
entry_point = ".yarn/releases/yarn-1.22.22.cjs",
|
||||
visibility = [
|
||||
"//integration:__subpackages__",
|
||||
"//modules/ssr-benchmarks:__subpackages__",
|
||||
],
|
||||
)
|
||||
|
||||
alias(
|
||||
name = "sauce_connect",
|
||||
actual = select({
|
||||
|
||||
+185
-154
@@ -1,52 +1,180 @@
|
||||
<a name="20.2.0-next.6"></a>
|
||||
# 20.2.0-next.6 (2025-08-07)
|
||||
<a name="20.3.2"></a>
|
||||
# 20.3.2 (2025-09-24)
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [812463c563](https://github.com/angular/angular/commit/812463c5636effe5bd5ba5c7c7fc65c3cc08d047) | fix | Ignore diagnostics on ngTemplateContextGuard lines in TCB ([#63054](https://github.com/angular/angular/pull/63054)) |
|
||||
| [45b030b5ce](https://github.com/angular/angular/commit/45b030b5ce1e116a88fe1c2fe133f654fb1f66c5) | fix | prevent dom event assertion in TCB generation on older angular versions ([#63053](https://github.com/angular/angular/pull/63053)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="20.2.0-next.5"></a>
|
||||
# 20.2.0-next.5 (2025-08-06)
|
||||
## Deprecations
|
||||
### router
|
||||
- The Router.getCurrentNavigation method is deprecated. Use the Router.currentNavigation signal instead.
|
||||
### platform-browser
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [d24d5742db](https://github.com/angular/angular/commit/d24d5742db694604b09d57a3503e8c32751c4836) | feat | Add IsolatedShadowDom encapsulation method ([#62723](https://github.com/angular/angular/pull/62723)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [687c374826](https://github.com/angular/angular/commit/687c374826c5e9ea91839c20f0df815ce085c583) | feat | add a currentNavigation signal to the Router service. ([#63011](https://github.com/angular/angular/pull/63011)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="20.1.6"></a>
|
||||
# 20.1.6 (2025-08-06)
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="20.2.0-next.4"></a>
|
||||
# 20.2.0-next.4 (2025-08-06)
|
||||
## Deprecations
|
||||
### router
|
||||
- The Router.getCurrentNavigation method is deprecated. Use the Router.currentNavigation signal instead.
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [fb1fc823c4](https://github.com/angular/angular/commit/fb1fc823c47ef34347ba758dd73cc997c7f0c009) | fix | correctly type check host listeners to own outputs ([#62965](https://github.com/angular/angular/pull/62965)) |
|
||||
| [ba40153ac0](https://github.com/angular/angular/commit/ba40153ac07fc721585a1224fda09a654672cb74) | fix | capture metadata for undecorated fields ([#63904](https://github.com/angular/angular/pull/63904)) |
|
||||
| [1d4f81c8ee](https://github.com/angular/angular/commit/1d4f81c8eedf5ea69c51c720f8dc5c5d12a62ba2) | fix | resolve import alias in defer blocks ([#63966](https://github.com/angular/angular/pull/63966)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [9515a70933](https://github.com/angular/angular/commit/9515a709331883f0ca9857ed46a5262b01979a26) | fix | fix narrowing of `Resource.hasValue()` ([#63994](https://github.com/angular/angular/pull/63994)) |
|
||||
| [e78451cf8a](https://github.com/angular/angular/commit/e78451cf8a48322879e83b33fecc0b5854947afb) | fix | prevent animations renderer from impacting `animate.leave` ([#63921](https://github.com/angular/angular/pull/63921)) |
|
||||
### forms
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [1fd8d5d446](https://github.com/angular/angular/commit/1fd8d5d446f909a16a127ba117a0f423c7a5db0c) | fix | Emit `FormResetEvent` when resetting control ([#64034](https://github.com/angular/angular/pull/64034)) |
|
||||
### migrations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [16d0d43ad4](https://github.com/angular/angular/commit/16d0d43ad4903b69b8dcd9b76c48b5089e7f82ee) | fix | handle import aliases to the same module name ([#63934](https://github.com/angular/angular/pull/63934)) |
|
||||
| [3ebaeccb46](https://github.com/angular/angular/commit/3ebaeccb466119ee43eeaa486f5e132c85e9caa2) | fix | handle reused templates in control flow migration ([#63996](https://github.com/angular/angular/pull/63996)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="20.3.1"></a>
|
||||
# 20.3.1 (2025-09-17)
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [7fb5a8087e](https://github.com/angular/angular/commit/7fb5a8087ee8fb0451cedbe6ac4ce972eca4b56e) | fix | Add support for `aria-invalid` ([#63748](https://github.com/angular/angular/pull/63748)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [8843707919](https://github.com/angular/angular/commit/88437079190cef9ee522a3e2defa6e2672c2d030) | fix | only bind inputs that are part of microsyntax to a structural directive ([#52453](https://github.com/angular/angular/pull/52453)) |
|
||||
| [38c9921ff3](https://github.com/angular/angular/commit/38c9921ff387d235981a79e26dc8bc7e60a2e10c) | fix | signal not invoked diagnostic not raised when input has same name in template ([#63754](https://github.com/angular/angular/pull/63754)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [802dbcc2a0](https://github.com/angular/angular/commit/802dbcc2a0c5d3784cb04b4c78ea71ed0925327c) | fix | prevent animation events from being cleaned up on destroy ([#63414](https://github.com/angular/angular/pull/63414)) |
|
||||
| [3ec8a5c753](https://github.com/angular/angular/commit/3ec8a5c7536cdd2c1db7db4bfbc2d4995156a833) | fix | Prevent leave animations on a move operation ([#63745](https://github.com/angular/angular/pull/63745)) |
|
||||
### migrations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [6e54bdfdcb](https://github.com/angular/angular/commit/6e54bdfdcb01522ee46865fadec911f960fff730) | fix | fix route-lazy-loading migration ([#63818](https://github.com/angular/angular/pull/63818)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="20.3.0"></a>
|
||||
# 20.3.0 (2025-09-10)
|
||||
## Breaking Changes
|
||||
### core
|
||||
- The server-side bootstrapping process has been changed to eliminate the reliance on a global platform injector.
|
||||
|
||||
Before:
|
||||
```ts
|
||||
const bootstrap = () => bootstrapApplication(AppComponent, config);
|
||||
```
|
||||
|
||||
After:
|
||||
```ts
|
||||
const bootstrap = (context: BootstrapContext) =>
|
||||
bootstrapApplication(AppComponent, config, context);
|
||||
```
|
||||
|
||||
A schematic is provided to automatically update `main.server.ts` files to pass the `BootstrapContext` to the `bootstrapApplication` call.
|
||||
|
||||
In addition, `getPlatform()` and `destroyPlatform()` will now return `null` and be a no-op respectively when running in a server environment.
|
||||
|
||||
(cherry picked from commit 8bf80c9d2314b4f2bcf3df83ae01552a6fc49834)
|
||||
###
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [a3f808d7c8](https://github.com/angular/angular/commit/a3f808d7c8cc59a4fd69f2e4b8d21a6510efa046) | fix | remove refresh button from transfer state tab ([#63592](https://github.com/angular/angular/pull/63592)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [6117ccee2e](https://github.com/angular/angular/commit/6117ccee2e1507fb00549cd70e064282645db803) | feat | introduce `BootstrapContext` for improved server bootstrapping ([#63636](https://github.com/angular/angular/pull/63636)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="20.2.4"></a>
|
||||
# 20.2.4 (2025-09-03)
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [dc64f3e478](https://github.com/angular/angular/commit/dc64f3e478c5cc1e354a0ff7cf5965b817b345d6) | fix | Fixed inject migration schematics for migrate destructured properties ([#62832](https://github.com/angular/angular/pull/62832)) |
|
||||
### platform-server
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [d1d32db972](https://github.com/angular/angular/commit/d1d32db97260c1e57c2937588002feb4271c7774) | fix | prevent false warning for duplicate state serialization ([#63525](https://github.com/angular/angular/pull/63525)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="20.2.3"></a>
|
||||
# 20.2.3 (2025-08-29)
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [479a919f42](https://github.com/angular/angular/commit/479a919f42517193653384220adab5b89dd74e3d) | fix | fixes regression with event parsing and animate prefix ([#63470](https://github.com/angular/angular/pull/63470)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [f87fad3fff](https://github.com/angular/angular/commit/f87fad3fff62cebf2868e06cba48e0f27b719d24) | fix | avoid injecting internal error handler from a destroyed injector ([#62275](https://github.com/angular/angular/pull/62275)) |
|
||||
| [114906d2d6](https://github.com/angular/angular/commit/114906d2d68d98c98961d858abd3ae714d4809a3) | fix | Fix cancellation of animation enter classes ([#63442](https://github.com/angular/angular/pull/63442)) |
|
||||
| [596b545130](https://github.com/angular/angular/commit/596b5451309b8ce4f08a1cd36e6b3610507d52f9) | fix | Prevent an error on cleanup when an `rxResource` `stream` threw before returning an `Observable` ([#63342](https://github.com/angular/angular/pull/63342)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="20.2.2"></a>
|
||||
# 20.2.2 (2025-08-27)
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [d7b6045d61](https://github.com/angular/angular/commit/d7b6045d61582d20a17802e769dc1441984988f0) | fix | fixes animations on elements with structural directives ([#63390](https://github.com/angular/angular/pull/63390)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [6c421ed65d](https://github.com/angular/angular/commit/6c421ed65d050765a18eafc51fe7257abc5682ce) | fix | Ensures `@for` loop animations never get cancelled ([#63328](https://github.com/angular/angular/pull/63328)) |
|
||||
| [9093e0e132](https://github.com/angular/angular/commit/9093e0e132f99c2b590c31b299871bcd493b7de0) | fix | fix memory leak with leaving nodes tracking ([#63328](https://github.com/angular/angular/pull/63328)) |
|
||||
| [c8f07daf8f](https://github.com/angular/angular/commit/c8f07daf8f2c7e8c6641eb4368379a3f5f1d1f52) | fix | Fixes `animate.leave` binding to a string with spaces ([#63366](https://github.com/angular/angular/pull/63366)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="20.2.1"></a>
|
||||
# 20.2.1 (2025-08-21)
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [a28672fb70](https://github.com/angular/angular/commit/a28672fb7017cc62e42829c5910c3b39373d7913) | fix | Keep paraenthesis in Nullish + Boolean expression. ([#63292](https://github.com/angular/angular/pull/63292)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="20.2.0"></a>
|
||||
# 20.2.0 (2025-08-20)
|
||||
## Deprecations
|
||||
### animations
|
||||
- @angular/animations
|
||||
### core
|
||||
- @angular/animations
|
||||
### router
|
||||
- The Router.getCurrentNavigation method is deprecated. Use the Router.currentNavigation signal instead.
|
||||
- The Router.getCurrentNavigation method is deprecated. Use the Router.currentNavigation signal instead.
|
||||
### animations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [9766116cea](https://github.com/angular/angular/commit/9766116cea69607d80144251a599f1cc1b12e02c) | refactor | deprecate the animations package ([#62795](https://github.com/angular/angular/pull/62795)) |
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [7767aa640c](https://github.com/angular/angular/commit/7767aa640c542f5058df9322f2bbe974fa8d3c81) | fix | allow more characters in square-bracketed attribute names ([#62742](https://github.com/angular/angular/pull/62742)) |
|
||||
| [7b51728813](https://github.com/angular/angular/commit/7b517288139aec166e5e5b60e84b1e22e3d6b70f) | fix | fixes animation event host bindings not firing ([#63217](https://github.com/angular/angular/pull/63217)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [5abfe4a899](https://github.com/angular/angular/commit/5abfe4a8999e42ad44e6f1d4414f241094bb8fdb) | feat | add diagnostic for uninvoked functions in text interpolation ([#59191](https://github.com/angular/angular/pull/59191)) |
|
||||
| [c4917074f1](https://github.com/angular/angular/commit/c4917074f1e278ea24948a8810b3d4f306765174) | fix | display proper function in NG8117 message ([#62842](https://github.com/angular/angular/pull/62842)) |
|
||||
| [812463c563](https://github.com/angular/angular/commit/812463c5636effe5bd5ba5c7c7fc65c3cc08d047) | fix | Ignore diagnostics on ngTemplateContextGuard lines in TCB ([#63054](https://github.com/angular/angular/pull/63054)) |
|
||||
| [45b030b5ce](https://github.com/angular/angular/commit/45b030b5ce1e116a88fe1c2fe133f654fb1f66c5) | fix | prevent dom event assertion in TCB generation on older angular versions ([#63053](https://github.com/angular/angular/pull/63053)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [6b1f4b9e8b](https://github.com/angular/angular/commit/6b1f4b9e8bb981377e271e6af0d9768ff7f765e9) | feat | add enter and leave animation instructions ([#62682](https://github.com/angular/angular/pull/62682)) |
|
||||
| [cec91c0035](https://github.com/angular/angular/commit/cec91c00356ee3974c39c9471b243a2a16149f5b) | feat | add option to infer the tag names of components in tests ([#62283](https://github.com/angular/angular/pull/62283)) |
|
||||
| [141bb75ff2](https://github.com/angular/angular/commit/141bb75ff241425a93ce5b60b56a4247e67d7648) | feat | Promote zoneless to stable ([#62699](https://github.com/angular/angular/pull/62699)) |
|
||||
| [4138aca91f](https://github.com/angular/angular/commit/4138aca91fe828f0cfbd779d0c456cdea7703bdc) | feat | render ARIA property bindings as attributes ([#62630](https://github.com/angular/angular/pull/62630)) |
|
||||
| [a409534d6c](https://github.com/angular/angular/commit/a409534d6c3d7cb4472afffd6b17df8c25e34106) | feat | support `as` aliases on `else if` blocks ([#63047](https://github.com/angular/angular/pull/63047)) |
|
||||
| [745ea44394](https://github.com/angular/angular/commit/745ea4439465494ab5b7002dd1fa320cd32220fb) | feat | support TypeScript 5.9 ([#62541](https://github.com/angular/angular/pull/62541)) |
|
||||
| [593cc8a368](https://github.com/angular/angular/commit/593cc8a3684dfb163bfffa265c5efb3bc7efacd1) | fix | checks if body exists before continuing ([#62768](https://github.com/angular/angular/pull/62768)) |
|
||||
| [bdc31675b7](https://github.com/angular/angular/commit/bdc31675b7e5f37d2b312c766fe4963305620bdf) | fix | ensure animate events do not have duplicate elements ([#63216](https://github.com/angular/angular/pull/63216)) |
|
||||
| [de3a0c5cf3](https://github.com/angular/angular/commit/de3a0c5cf3f87782fa63d30edf6ac05eb6be9fac) | fix | Fix `animate.enter` class removal when composing classes ([#62981](https://github.com/angular/angular/pull/62981)) |
|
||||
| [6597ac0af7](https://github.com/angular/angular/commit/6597ac0af78ac2224ec2f9a37283b53aee11abe1) | fix | fix support for space separated strings in leave animations ([#62979](https://github.com/angular/angular/pull/62979)) |
|
||||
| [ebd622b344](https://github.com/angular/angular/commit/ebd622b3449789b72efc8295244ca924a299e7c1) | fix | fixes empty animations when recalculating styles ([#63007](https://github.com/angular/angular/pull/63007)) |
|
||||
| [455b147488](https://github.com/angular/angular/commit/455b147488dc0a064c0ca13a96a4df3c3ed01152) | fix | fixes timing issues with enter animations ([#62925](https://github.com/angular/angular/pull/62925)) |
|
||||
| [648bbb097e](https://github.com/angular/angular/commit/648bbb097e858fd1276b6ee88c82b674341e6163) | fix | properly recognize failed `fetch` responses when loading external resources in JIT ([#62992](https://github.com/angular/angular/pull/62992)) |
|
||||
| [f9d73cc687](https://github.com/angular/angular/commit/f9d73cc6877d516da4ab4704c21bb19164123fa1) | fix | handle cases where classes added have no animations ([#63242](https://github.com/angular/angular/pull/63242)) |
|
||||
| [6a1184600c](https://github.com/angular/angular/commit/6a1184600ce0fc7a3f338d6766612e9510ef5518) | fix | prevents duplicate nodes when `@if` toggles with leave animations ([#63048](https://github.com/angular/angular/pull/63048)) |
|
||||
| [063b5e166f](https://github.com/angular/angular/commit/063b5e166f66bce1abd06c258242212009e76cca) | fix | switch check to documentElement with chaining ([#62773](https://github.com/angular/angular/pull/62773)) |
|
||||
| [320de4e96d](https://github.com/angular/angular/commit/320de4e96d250cad1ce2c9f8c0fa2022da53b734) | refactor | deprecate animations field on component interface ([#62895](https://github.com/angular/angular/pull/62895)) |
|
||||
### forms
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
@@ -54,11 +182,16 @@
|
||||
### http
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [e5d6fb5825](https://github.com/angular/angular/commit/e5d6fb5825d5e1b76d73d5c1c64c9986cfb02874) | fix | Reset headers, progress, and statusCode when using `set()` in `HttpResource` ([#62873](https://github.com/angular/angular/pull/62873)) |
|
||||
### migrations
|
||||
| [0984b30388](https://github.com/angular/angular/commit/0984b30388ef51dfad66f1228f665b89b73ef3fb) | feat | Add redirected property to HttpResponse and HttpErrorResponse ([#62675](https://github.com/angular/angular/pull/62675)) |
|
||||
| [be811fee79](https://github.com/angular/angular/commit/be811fee7925fb482567fa7cd9d485ac28acdade) | feat | add referrer & integrity support for fetch requests in httpResource ([#62461](https://github.com/angular/angular/pull/62461)) |
|
||||
| [1cf9d9064c](https://github.com/angular/angular/commit/1cf9d9064c15c00071ece3b78c8019035a6db6ce) | feat | Add support for fetch referrer & integrity options in HttpClient ([#62417](https://github.com/angular/angular/pull/62417)) |
|
||||
| [1408baff45](https://github.com/angular/angular/commit/1408baff453e636da05838fa17c6e4abd86c4b72) | fix | Add missing timeout and transferCache options to `HttpClient` ([#62586](https://github.com/angular/angular/pull/62586)) |
|
||||
### language-service
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [b1514d512e](https://github.com/angular/angular/commit/b1514d512e041674d1ebbac88eb2d9f6a0f2cf5e) | fix | incorrect filtering in inject migration ([#62913](https://github.com/angular/angular/pull/62913)) |
|
||||
| [c81e345e72](https://github.com/angular/angular/commit/c81e345e726b5b281621159c789e6d80a9f328e2) | feat | support auto-import for attribute completions ([#62797](https://github.com/angular/angular/pull/62797)) |
|
||||
| [d64dd27a02](https://github.com/angular/angular/commit/d64dd27a02630b631bc9890d7292d4683493cb65) | feat | support to report the deprecated API in the template ([#62054](https://github.com/angular/angular/pull/62054)) |
|
||||
| [591c7e2ec8](https://github.com/angular/angular/commit/591c7e2ec82c6669ffa6e0011b8a0a4fc12e9c3a) | fix | Support to resolve the re-export component. ([#62585](https://github.com/angular/angular/pull/62585)) |
|
||||
### platform-browser
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
@@ -67,11 +200,22 @@
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [d00b3fed58](https://github.com/angular/angular/commit/d00b3fed58496369d9f3a1ac0d74416a586be78b) | feat | add a `currentNavigation` signal to the `Router` service. ([#62971](https://github.com/angular/angular/pull/62971)) |
|
||||
| [687c374826](https://github.com/angular/angular/commit/687c374826c5e9ea91839c20f0df815ce085c583) | feat | add a currentNavigation signal to the Router service. ([#63011](https://github.com/angular/angular/pull/63011)) |
|
||||
| [9c45c322d1](https://github.com/angular/angular/commit/9c45c322d1ac3b05c916b7c956263066fb9be47f) | fix | ensure preloaded components are properly activated ([#62502](https://github.com/angular/angular/pull/62502)) |
|
||||
### service-worker
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [8255e0cf15](https://github.com/angular/angular/commit/8255e0cf15353e9eee339ae01851e32c0e5e174d) | feat | add messageerror event handling and logging ([#62834](https://github.com/angular/angular/pull/62834)) |
|
||||
| [5220b51e75](https://github.com/angular/angular/commit/5220b51e75e672ff41c90f4798289961973df8e0) | feat | Adds for type in provideServiceWorker ([#62831](https://github.com/angular/angular/pull/62831)) |
|
||||
| [4ac6171b09](https://github.com/angular/angular/commit/4ac6171b09e449c619e0588c366861f8f3bb59be) | feat | Adds support for updateViaCache in provideServiceWorker ([#62721](https://github.com/angular/angular/pull/62721)) |
|
||||
| [b65c3d5e19](https://github.com/angular/angular/commit/b65c3d5e195267dd90b2826d4615ced1328b1709) | feat | Improves storage full detection in data caching ([#62737](https://github.com/angular/angular/pull/62737)) |
|
||||
| [3b214d2040](https://github.com/angular/angular/commit/3b214d20403160ab73e65dca0352545efd577c31) | feat | Logs unhandled promise rejections in service worker ([#63059](https://github.com/angular/angular/pull/63059)) |
|
||||
| [6d011687ec](https://github.com/angular/angular/commit/6d011687ec1fa2b8f0211379bb98adc8e02f4e9a) | feat | notify clients about version failures ([#62718](https://github.com/angular/angular/pull/62718)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="20.1.6"></a>
|
||||
# 20.1.6 (2025-08-06)
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
@@ -96,44 +240,6 @@
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="20.2.0-next.3"></a>
|
||||
# 20.2.0-next.3 (2025-07-31)
|
||||
## Deprecations
|
||||
### animations
|
||||
- @angular/animations
|
||||
### core
|
||||
- @angular/animations
|
||||
### animations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [9766116cea](https://github.com/angular/angular/commit/9766116cea69607d80144251a599f1cc1b12e02c) | refactor | deprecate the animations package ([#62795](https://github.com/angular/angular/pull/62795)) |
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [5b25d93f27](https://github.com/angular/angular/commit/5b25d93f27b259098ed968a8a355e9c16867b37b) | fix | exclude more safe reads expression from 2way-binding ([#62852](https://github.com/angular/angular/pull/62852)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [c4917074f1](https://github.com/angular/angular/commit/c4917074f1e278ea24948a8810b3d4f306765174) | fix | display proper function in NG8117 message ([#62842](https://github.com/angular/angular/pull/62842)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [92c2d2a006](https://github.com/angular/angular/commit/92c2d2a0065a1d5a7e87fb54a4cd454f21bd8ab9) | fix | update symbols for new signals api ([#62284](https://github.com/angular/angular/pull/62284)) |
|
||||
| [320de4e96d](https://github.com/angular/angular/commit/320de4e96d250cad1ce2c9f8c0fa2022da53b734) | refactor | deprecate animations field on component interface ([#62895](https://github.com/angular/angular/pull/62895)) |
|
||||
### http
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [0984b30388](https://github.com/angular/angular/commit/0984b30388ef51dfad66f1228f665b89b73ef3fb) | feat | Add redirected property to HttpResponse and HttpErrorResponse ([#62675](https://github.com/angular/angular/pull/62675)) |
|
||||
| [245494ad29](https://github.com/angular/angular/commit/245494ad292ea0b61ad7e314c8913afdcd8ca837) | fix | add missing http options allowed in fetch API ([#62881](https://github.com/angular/angular/pull/62881)) |
|
||||
| [1b73c23597](https://github.com/angular/angular/commit/1b73c235977f37ca7fbc5f4be8b194054632352b) | fix | propagate plain errors when parsing fails ([#62765](https://github.com/angular/angular/pull/62765)) |
|
||||
### service-worker
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [b65c3d5e19](https://github.com/angular/angular/commit/b65c3d5e195267dd90b2826d4615ced1328b1709) | feat | Improves storage full detection in data caching ([#62737](https://github.com/angular/angular/pull/62737)) |
|
||||
| [6d011687ec](https://github.com/angular/angular/commit/6d011687ec1fa2b8f0211379bb98adc8e02f4e9a) | feat | notify clients about version failures ([#62718](https://github.com/angular/angular/pull/62718)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="20.1.4"></a>
|
||||
# 20.1.4 (2025-07-31)
|
||||
### compiler
|
||||
@@ -152,37 +258,6 @@
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="20.2.0-next.2"></a>
|
||||
# 20.2.0-next.2 (2025-07-23)
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [7767aa640c](https://github.com/angular/angular/commit/7767aa640c542f5058df9322f2bbe974fa8d3c81) | fix | allow more characters in square-bracketed attribute names ([#62742](https://github.com/angular/angular/pull/62742)) |
|
||||
| [3171d01452](https://github.com/angular/angular/commit/3171d01452444721dbea53d1d1ca758f38d083a5) | fix | fix detection of directive deps in JIT ([#62666](https://github.com/angular/angular/pull/62666)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [5abfe4a899](https://github.com/angular/angular/commit/5abfe4a8999e42ad44e6f1d4414f241094bb8fdb) | feat | add diagnostic for uninvoked functions in text interpolation ([#59191](https://github.com/angular/angular/pull/59191)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [6b1f4b9e8b](https://github.com/angular/angular/commit/6b1f4b9e8bb981377e271e6af0d9768ff7f765e9) | feat | add enter and leave animation instructions ([#62682](https://github.com/angular/angular/pull/62682)) |
|
||||
| [cec91c0035](https://github.com/angular/angular/commit/cec91c00356ee3974c39c9471b243a2a16149f5b) | feat | add option to infer the tag names of components in tests ([#62283](https://github.com/angular/angular/pull/62283)) |
|
||||
| [4138aca91f](https://github.com/angular/angular/commit/4138aca91fe828f0cfbd779d0c456cdea7703bdc) | feat | render ARIA property bindings as attributes ([#62630](https://github.com/angular/angular/pull/62630)) |
|
||||
| [593cc8a368](https://github.com/angular/angular/commit/593cc8a3684dfb163bfffa265c5efb3bc7efacd1) | fix | checks if body exists before continuing ([#62768](https://github.com/angular/angular/pull/62768)) |
|
||||
| [b8e415a717](https://github.com/angular/angular/commit/b8e415a717fd3747c7bab3f6ea652874082a19bb) | fix | fix change tracking for Resource#hasValue ([#62595](https://github.com/angular/angular/pull/62595)) |
|
||||
| [063b5e166f](https://github.com/angular/angular/commit/063b5e166f66bce1abd06c258242212009e76cca) | fix | switch check to documentElement with chaining ([#62773](https://github.com/angular/angular/pull/62773)) |
|
||||
### platform-browser
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [74d99ed379](https://github.com/angular/angular/commit/74d99ed3796a3ced462e327c9b5857e5de4b7cb9) | fix | resolve component resources before bootstrapping in JIT mode ([#62758](https://github.com/angular/angular/pull/62758)) |
|
||||
### service-worker
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [4ac6171b09](https://github.com/angular/angular/commit/4ac6171b09e449c619e0588c366861f8f3bb59be) | feat | Adds support for updateViaCache in provideServiceWorker ([#62721](https://github.com/angular/angular/pull/62721)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="20.1.3"></a>
|
||||
# 20.1.3 (2025-07-23)
|
||||
### core
|
||||
@@ -205,41 +280,6 @@
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="20.2.0-next.1"></a>
|
||||
# 20.2.0-next.1 (2025-07-16)
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [a1e3f2bcd1](https://github.com/angular/angular/commit/a1e3f2bcd14f897e685e11a84e011406dd6f7a33) | fix | incorrect spans for left side of binary operation ([#62641](https://github.com/angular/angular/pull/62641)) |
|
||||
| [18a675081f](https://github.com/angular/angular/commit/18a675081f945ed4d7f4c0ee83172ca1528e7b2e) | fix | more permissive parsing of @ characters ([#62644](https://github.com/angular/angular/pull/62644)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [527bd778a7](https://github.com/angular/angular/commit/527bd778a7b23c23766bad7fb272d61df9a884b1) | fix | infer type of event target for void elements ([#62648](https://github.com/angular/angular/pull/62648)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [745ea44394](https://github.com/angular/angular/commit/745ea4439465494ab5b7002dd1fa320cd32220fb) | feat | support TypeScript 5.9 ([#62541](https://github.com/angular/angular/pull/62541)) |
|
||||
| [859958dbfe](https://github.com/angular/angular/commit/859958dbfe5b8b505c84ecfed81d43e6c59e6f0c) | fix | Ensure application remains unstable during bootstrap ([#62631](https://github.com/angular/angular/pull/62631)) |
|
||||
| [780774f948](https://github.com/angular/angular/commit/780774f94806b8eb895cb2a77c0d2a154f3d2d22) | fix | InputBinding marks component a dirty. ([#62613](https://github.com/angular/angular/pull/62613)) |
|
||||
### http
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [be811fee79](https://github.com/angular/angular/commit/be811fee7925fb482567fa7cd9d485ac28acdade) | feat | add referrer & integrity support for fetch requests in httpResource ([#62461](https://github.com/angular/angular/pull/62461)) |
|
||||
| [1408baff45](https://github.com/angular/angular/commit/1408baff453e636da05838fa17c6e4abd86c4b72) | fix | Add missing timeout and transferCache options to `HttpClient` ([#62586](https://github.com/angular/angular/pull/62586)) |
|
||||
| [75a5d08c5e](https://github.com/angular/angular/commit/75a5d08c5e9947f835ef911aacfe6c673e4c9e94) | fix | do not display warnings `Angular detected that a `HttpClient` request with the `keepalive` option was sent using XHR` when option is not true ([#62536](https://github.com/angular/angular/pull/62536)) |
|
||||
### language-service
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [d64dd27a02](https://github.com/angular/angular/commit/d64dd27a02630b631bc9890d7292d4683493cb65) | feat | support to report the deprecated API in the template ([#62054](https://github.com/angular/angular/pull/62054)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [9c45c322d1](https://github.com/angular/angular/commit/9c45c322d1ac3b05c916b7c956263066fb9be47f) | fix | ensure preloaded components are properly activated ([#62502](https://github.com/angular/angular/pull/62502)) |
|
||||
| [e9f215624f](https://github.com/angular/angular/commit/e9f215624f73c6aec6ec78117339c803830f4114) | fix | handle errors from view transition readiness ([#62535](https://github.com/angular/angular/pull/62535)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="20.1.1"></a>
|
||||
# 20.1.1 (2025-07-16)
|
||||
### compiler
|
||||
@@ -267,15 +307,6 @@
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="20.2.0-next.0"></a>
|
||||
# 20.2.0-next.0 (2025-07-09)
|
||||
### http
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [1cf9d9064c](https://github.com/angular/angular/commit/1cf9d9064c15c00071ece3b78c8019035a6db6ce) | feat | Add support for fetch referrer & integrity options in HttpClient ([#62417](https://github.com/angular/angular/pull/62417)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="20.1.0"></a>
|
||||
# 20.1.0 (2025-07-09)
|
||||
### common
|
||||
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
"""Rules/toolchains for angular with Bazel."""
|
||||
|
||||
module(
|
||||
name = "angular",
|
||||
)
|
||||
|
||||
bazel_dep(name = "rules_pkg", version = "1.1.0")
|
||||
bazel_dep(name = "rules_nodejs", version = "6.5.0")
|
||||
bazel_dep(name = "aspect_rules_ts", version = "3.7.0")
|
||||
bazel_dep(name = "aspect_rules_js", version = "2.6.0")
|
||||
bazel_dep(name = "aspect_rules_esbuild", version = "0.22.1")
|
||||
bazel_dep(name = "aspect_rules_jasmine", version = "2.0.0")
|
||||
bazel_dep(name = "aspect_rules_rollup", version = "2.0.1")
|
||||
bazel_dep(name = "bazel_skylib", version = "1.8.1")
|
||||
bazel_dep(name = "aspect_bazel_lib", version = "2.21.2")
|
||||
bazel_dep(name = "tar.bzl", version = "0.5.5")
|
||||
bazel_dep(name = "yq.bzl", version = "0.2.0")
|
||||
bazel_dep(name = "rules_angular")
|
||||
git_override(
|
||||
module_name = "rules_angular",
|
||||
commit = "4010ef96de0c46db7764adc2f262258c9de3d718",
|
||||
remote = "https://github.com/devversion/rules_angular.git",
|
||||
)
|
||||
|
||||
bazel_dep(name = "devinfra")
|
||||
git_override(
|
||||
module_name = "devinfra",
|
||||
commit = "ad9c933cb115f77a73c0d78a82328448a66ad89c",
|
||||
remote = "https://github.com/angular/dev-infra.git",
|
||||
)
|
||||
|
||||
bazel_dep(name = "rules_sass")
|
||||
git_override(
|
||||
module_name = "rules_sass",
|
||||
commit = "4a54e0e4e75fc6456ff874e53fb4a78bdfeb44b9",
|
||||
remote = "https://github.com/devversion/rules_sass.git",
|
||||
)
|
||||
|
||||
bazel_dep(name = "rules_browsers")
|
||||
git_override(
|
||||
module_name = "rules_browsers",
|
||||
commit = "6749ba4c0dc5e04536369fa91fdb2d827b6705ff",
|
||||
remote = "https://github.com/devversion/rules_browsers.git",
|
||||
)
|
||||
|
||||
yq = use_extension("@yq.bzl//yq:extensions.bzl", "yq")
|
||||
use_repo(yq, "yq_toolchains")
|
||||
|
||||
node = use_extension("@rules_nodejs//nodejs:extensions.bzl", "node")
|
||||
node.toolchain(node_version = "20.19.0")
|
||||
use_repo(node, "nodejs_toolchains")
|
||||
use_repo(node, "nodejs_darwin_amd64")
|
||||
use_repo(node, "nodejs_darwin_arm64")
|
||||
use_repo(node, "nodejs_linux_amd64")
|
||||
use_repo(node, "nodejs_linux_arm64")
|
||||
use_repo(node, "nodejs_linux_ppc64le")
|
||||
use_repo(node, "nodejs_linux_s390x")
|
||||
use_repo(node, "nodejs_windows_amd64")
|
||||
|
||||
pnpm = use_extension("@aspect_rules_js//npm:extensions.bzl", "pnpm")
|
||||
pnpm.pnpm(
|
||||
name = "pnpm",
|
||||
pnpm_version = "10.16.1",
|
||||
)
|
||||
use_repo(pnpm, "pnpm")
|
||||
|
||||
npm = use_extension("@aspect_rules_js//npm:extensions.bzl", "npm")
|
||||
npm.npm_translate_lock(
|
||||
name = "npm",
|
||||
data = [
|
||||
"//:.pnpmfile.cjs",
|
||||
"//:package.json",
|
||||
"//:pnpm-workspace.yaml",
|
||||
"//:tools/pnpm-patches/dagre-d3-es+7.0.11.patch",
|
||||
"//adev:package.json",
|
||||
"//adev/shared-docs:package.json",
|
||||
"//adev/shared-docs/pipeline/api-gen:package.json",
|
||||
"//integration:package.json",
|
||||
"//modules:package.json",
|
||||
"//packages/animations:package.json",
|
||||
"//packages/benchpress:package.json",
|
||||
"//packages/common:package.json",
|
||||
"//packages/compiler:package.json",
|
||||
"//packages/compiler-cli:package.json",
|
||||
"//packages/compiler-cli/linker/babel/test:package.json",
|
||||
"//packages/core:package.json",
|
||||
"//packages/core/test/bundling:package.json",
|
||||
"//packages/elements:package.json",
|
||||
"//packages/forms:package.json",
|
||||
"//packages/language-service:package.json",
|
||||
"//packages/localize:package.json",
|
||||
"//packages/platform-browser:package.json",
|
||||
"//packages/platform-browser-dynamic:package.json",
|
||||
"//packages/platform-server:package.json",
|
||||
"//packages/router:package.json",
|
||||
"//packages/service-worker:package.json",
|
||||
"//packages/upgrade:package.json",
|
||||
"//tools/bazel/rules_angular_store:package.json",
|
||||
],
|
||||
npmrc = "//:.npmrc",
|
||||
pnpm_lock = "//:pnpm-lock.yaml",
|
||||
verify_node_modules_ignored = "//:.bazelignore",
|
||||
)
|
||||
use_repo(npm, "npm")
|
||||
|
||||
rules_ts_ext = use_extension("@aspect_rules_ts//ts:extensions.bzl", "ext")
|
||||
rules_ts_ext.deps(
|
||||
name = "angular_npm_typescript",
|
||||
# Obtained by: curl --silent https://registry.npmjs.org/typescript/5.9.2 | jq -r '.dist.integrity'
|
||||
ts_integrity = "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||
ts_version = "5.9.2",
|
||||
)
|
||||
use_repo(rules_ts_ext, **{"npm_typescript": "angular_npm_typescript"})
|
||||
|
||||
# TODO: Figure out how to make ng_project update whenever the packages/core::pkg target changes.
|
||||
rules_angular = use_extension("@rules_angular//setup:extensions.bzl", "rules_angular")
|
||||
|
||||
use_repo_rule("@rules_angular//setup:repositories.bzl", "configurable_deps_repo")(
|
||||
name = "rules_angular_configurable_deps",
|
||||
angular_compiler_cli = "@angular//:node_modules/@angular/compiler-cli",
|
||||
typescript = "@angular//:node_modules/typescript",
|
||||
)
|
||||
|
||||
override_repo(rules_angular, "rules_angular_configurable_deps")
|
||||
|
||||
register_toolchains(
|
||||
"@devinfra//bazel/git-toolchain:git_linux_toolchain",
|
||||
"@devinfra//bazel/git-toolchain:git_macos_x86_toolchain",
|
||||
"@devinfra//bazel/git-toolchain:git_macos_arm64_toolchain",
|
||||
"@devinfra//bazel/git-toolchain:git_windows_toolchain",
|
||||
)
|
||||
|
||||
CLDR_VERSION = "41"
|
||||
|
||||
cldr_json_data = use_repo_rule("//packages/common/locales/generate-locales-tool:cldr-data.bzl", "cldr_json_data_repository")
|
||||
|
||||
cldr_json_data(
|
||||
name = "cldr_json_data",
|
||||
urls = {
|
||||
"https://github.com/unicode-org/cldr-json/releases/download/%s.0.0/cldr-%s.0.0-json-full.zip" % (CLDR_VERSION, CLDR_VERSION): "649b76647269e32b1b0a5f7b6eed52e9e63a1581f1afdcf4f6771e49c9713614",
|
||||
},
|
||||
)
|
||||
|
||||
cldr_xml_data = use_repo_rule("//packages/common/locales/generate-locales-tool:cldr-data.bzl", "cldr_xml_data_repository")
|
||||
|
||||
cldr_xml_data(
|
||||
name = "cldr_xml_data",
|
||||
urls = {
|
||||
"https://github.com/unicode-org/cldr/releases/download/release-%s/cldr-common-%s.0.zip" % (CLDR_VERSION, CLDR_VERSION): "823c6170c41e2de2c229574e8a436332d25f1c9723409867fe721e00bc92d853",
|
||||
},
|
||||
)
|
||||
|
||||
http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
|
||||
# Fetch sauce connect (tool to open Saucelabs tunnel for Saucelabs browser tests)
|
||||
http_archive(
|
||||
name = "sauce_connect_linux_amd64",
|
||||
build_file_content = """exports_files(["bin/sc"], visibility = ["//visibility:public"])""",
|
||||
sha256 = "26b9c3630f441b47854b6032f7eca6f1d88d3f62e50ee44c27015d71a5155c36",
|
||||
strip_prefix = "sc-4.8.2-linux",
|
||||
url = "https://saucelabs.com/downloads/sc-4.8.2-linux.tar.gz",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "sauce_connect_mac",
|
||||
build_file_content = """exports_files(["bin/sc"], visibility = ["//visibility:public"])""",
|
||||
sha256 = "28277ce81ef9ab84f5b87b526258920a8ead44789a5034346e872629bbf38089",
|
||||
strip_prefix = "sc-4.8.2-osx",
|
||||
url = "https://saucelabs.com/downloads/sc-4.8.2-osx.zip",
|
||||
)
|
||||
Generated
+4334
File diff suppressed because it is too large
Load Diff
@@ -1,257 +0,0 @@
|
||||
workspace(
|
||||
name = "angular",
|
||||
)
|
||||
|
||||
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
|
||||
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
|
||||
# The PKG rules are needed to build tar packages for integration tests. The builtin
|
||||
# rule in `@bazel_tools` is not Windows compatible and outdated.
|
||||
# NOTE: We cannot move past version 0.6.0 as pkg_tar no longer works on directories, which rules_nodejs
|
||||
# relies on for node_modules setup.
|
||||
http_archive(
|
||||
name = "rules_pkg",
|
||||
sha256 = "62eeb544ff1ef41d786e329e1536c1d541bb9bcad27ae984d57f18f314018e66",
|
||||
urls = [
|
||||
"https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.6.0/rules_pkg-0.6.0.tar.gz",
|
||||
"https://github.com/bazelbuild/rules_pkg/releases/download/0.6.0/rules_pkg-0.6.0.tar.gz",
|
||||
],
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "aspect_rules_js",
|
||||
sha256 = "304c51726b727d53277dd28fcda1b8e43b7e46818530b8d6265e7be98d5e2b25",
|
||||
strip_prefix = "rules_js-2.3.8",
|
||||
url = "https://github.com/aspect-build/rules_js/releases/download/v2.3.8/rules_js-v2.3.8.tar.gz",
|
||||
)
|
||||
|
||||
load("@aspect_rules_js//js:repositories.bzl", "rules_js_dependencies")
|
||||
|
||||
rules_js_dependencies()
|
||||
|
||||
load("@aspect_rules_js//js:toolchains.bzl", "rules_js_register_toolchains")
|
||||
|
||||
rules_js_register_toolchains(
|
||||
node_repositories = {
|
||||
"20.19.0-darwin_arm64": ("node-v20.19.0-darwin-arm64.tar.gz", "node-v20.19.0-darwin-arm64", "c016cd1975a264a29dc1b07c6fbe60d5df0a0c2beb4113c0450e3d998d1a0d9c"),
|
||||
"20.19.0-darwin_amd64": ("node-v20.19.0-darwin-x64.tar.gz", "node-v20.19.0-darwin-x64", "a8554af97d6491fdbdabe63d3a1cfb9571228d25a3ad9aed2df856facb131b20"),
|
||||
"20.19.0-linux_arm64": ("node-v20.19.0-linux-arm64.tar.xz", "node-v20.19.0-linux-arm64", "dbe339e55eb393955a213e6b872066880bb9feceaa494f4d44c7aac205ec2ab9"),
|
||||
"20.19.0-linux_ppc64le": ("node-v20.19.0-linux-ppc64le.tar.xz", "node-v20.19.0-linux-ppc64le", "84937108f005679e60b486ed8e801cebfe923f02b76d8e710463d32f82181f65"),
|
||||
"20.19.0-linux_s390x": ("node-v20.19.0-linux-s390x.tar.xz", "node-v20.19.0-linux-s390x", "11f8ee99d792a83bba7b29911e0229dd6cd5e88987d7416346067db1cc76d89a"),
|
||||
"20.19.0-linux_amd64": ("node-v20.19.0-linux-x64.tar.xz", "node-v20.19.0-linux-x64", "b4e336584d62abefad31baecff7af167268be9bb7dd11f1297112e6eed3ca0d5"),
|
||||
"20.19.0-windows_amd64": ("node-v20.19.0-win-x64.zip", "node-v20.19.0-win-x64", "be72284c7bc62de07d5a9fd0ae196879842c085f11f7f2b60bf8864c0c9d6a4f"),
|
||||
},
|
||||
node_version = "20.19.0",
|
||||
)
|
||||
|
||||
load("@aspect_rules_js//npm:repositories.bzl", "npm_translate_lock")
|
||||
|
||||
npm_translate_lock(
|
||||
name = "npm2",
|
||||
data = [
|
||||
"//:.pnpmfile.cjs",
|
||||
"//:package.json",
|
||||
"//:pnpm-workspace.yaml",
|
||||
"//:tools/pnpm-patches/dagre-d3-es+7.0.11.patch",
|
||||
"//adev:package.json",
|
||||
"//adev/shared-docs:package.json",
|
||||
"//adev/shared-docs/pipeline/api-gen:package.json",
|
||||
"//integration:package.json",
|
||||
"//modules:package.json",
|
||||
"//packages/animations:package.json",
|
||||
"//packages/benchpress:package.json",
|
||||
"//packages/common:package.json",
|
||||
"//packages/compiler:package.json",
|
||||
"//packages/compiler-cli:package.json",
|
||||
"//packages/compiler-cli/linker/babel/test:package.json",
|
||||
"//packages/core:package.json",
|
||||
"//packages/core/test/bundling:package.json",
|
||||
"//packages/elements:package.json",
|
||||
"//packages/forms:package.json",
|
||||
"//packages/language-service:package.json",
|
||||
"//packages/localize:package.json",
|
||||
"//packages/platform-browser:package.json",
|
||||
"//packages/platform-browser-dynamic:package.json",
|
||||
"//packages/platform-server:package.json",
|
||||
"//packages/router:package.json",
|
||||
"//packages/service-worker:package.json",
|
||||
"//packages/upgrade:package.json",
|
||||
"//tools/bazel/rules_angular_store:package.json",
|
||||
],
|
||||
npmrc = "//:.npmrc",
|
||||
pnpm_lock = "//:pnpm-lock.yaml",
|
||||
pnpm_version = "9.15.9",
|
||||
verify_node_modules_ignored = "//:.bazelignore",
|
||||
)
|
||||
|
||||
load("@npm2//:repositories.bzl", "npm_repositories")
|
||||
|
||||
npm_repositories()
|
||||
|
||||
http_archive(
|
||||
name = "aspect_rules_ts",
|
||||
sha256 = "09af62a0d46918d815b5f48b5ed0f5349b62c15fc42fcc3fef5c246504ff8d99",
|
||||
strip_prefix = "rules_ts-3.6.3",
|
||||
url = "https://github.com/aspect-build/rules_ts/releases/download/v3.6.3/rules_ts-v3.6.3.tar.gz",
|
||||
)
|
||||
|
||||
load("@aspect_rules_ts//ts:repositories.bzl", "rules_ts_dependencies")
|
||||
|
||||
rules_ts_dependencies(
|
||||
# Obtained by: curl --silent https://registry.npmjs.org/typescript/5.9.2 | jq -r '.dist.integrity'
|
||||
ts_integrity = "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
|
||||
ts_version_from = "//:package.json",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "aspect_rules_rollup",
|
||||
sha256 = "0b8ac7d97cd660eb9a275600227e9c4268f5904cba962939d1a6ce9a0a059d2e",
|
||||
strip_prefix = "rules_rollup-2.0.1",
|
||||
url = "https://github.com/aspect-build/rules_rollup/releases/download/v2.0.1/rules_rollup-v2.0.1.tar.gz",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "aspect_rules_jasmine",
|
||||
sha256 = "0d2f9c977842685895020cac721d8cc4f1b37aae15af46128cf619741dc61529",
|
||||
strip_prefix = "rules_jasmine-2.0.0",
|
||||
url = "https://github.com/aspect-build/rules_jasmine/releases/download/v2.0.0/rules_jasmine-v2.0.0.tar.gz",
|
||||
)
|
||||
|
||||
load("@aspect_rules_jasmine//jasmine:dependencies.bzl", "rules_jasmine_dependencies")
|
||||
|
||||
rules_jasmine_dependencies()
|
||||
|
||||
load("@aspect_bazel_lib//lib:repositories.bzl", "aspect_bazel_lib_dependencies")
|
||||
|
||||
aspect_bazel_lib_dependencies()
|
||||
|
||||
load("@rules_pkg//:deps.bzl", "rules_pkg_dependencies")
|
||||
|
||||
rules_pkg_dependencies()
|
||||
|
||||
load("//packages/common/locales/generate-locales-tool:cldr-data.bzl", "cldr_json_data_repository", "cldr_xml_data_repository")
|
||||
|
||||
cldr_major_version = "41"
|
||||
|
||||
cldr_json_data_repository(
|
||||
name = "cldr_json_data",
|
||||
urls = {
|
||||
"https://github.com/unicode-org/cldr-json/releases/download/%s.0.0/cldr-%s.0.0-json-full.zip" % (cldr_major_version, cldr_major_version): "649b76647269e32b1b0a5f7b6eed52e9e63a1581f1afdcf4f6771e49c9713614",
|
||||
},
|
||||
)
|
||||
|
||||
cldr_xml_data_repository(
|
||||
name = "cldr_xml_data",
|
||||
urls = {
|
||||
"https://github.com/unicode-org/cldr/releases/download/release-%s/cldr-common-%s.0.zip" % (cldr_major_version, cldr_major_version): "823c6170c41e2de2c229574e8a436332d25f1c9723409867fe721e00bc92d853",
|
||||
},
|
||||
)
|
||||
|
||||
# Fetch sauce connect (tool to open Saucelabs tunnel for Saucelabs browser tests)
|
||||
http_archive(
|
||||
name = "sauce_connect_linux_amd64",
|
||||
build_file_content = """exports_files(["bin/sc"], visibility = ["//visibility:public"])""",
|
||||
sha256 = "26b9c3630f441b47854b6032f7eca6f1d88d3f62e50ee44c27015d71a5155c36",
|
||||
strip_prefix = "sc-4.8.2-linux",
|
||||
url = "https://saucelabs.com/downloads/sc-4.8.2-linux.tar.gz",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "sauce_connect_mac",
|
||||
build_file_content = """exports_files(["bin/sc"], visibility = ["//visibility:public"])""",
|
||||
sha256 = "28277ce81ef9ab84f5b87b526258920a8ead44789a5034346e872629bbf38089",
|
||||
strip_prefix = "sc-4.8.2-osx",
|
||||
url = "https://saucelabs.com/downloads/sc-4.8.2-osx.zip",
|
||||
)
|
||||
|
||||
git_repository(
|
||||
name = "devinfra",
|
||||
commit = "17282887a46c27e9f297a3292f1fde5b35dfafc1",
|
||||
remote = "https://github.com/angular/dev-infra.git",
|
||||
)
|
||||
|
||||
load("@devinfra//bazel:setup_dependencies_1.bzl", "setup_dependencies_1")
|
||||
|
||||
setup_dependencies_1()
|
||||
|
||||
load("@devinfra//bazel:setup_dependencies_2.bzl", "setup_dependencies_2")
|
||||
|
||||
setup_dependencies_2()
|
||||
|
||||
git_repository(
|
||||
name = "rules_angular",
|
||||
commit = "a1b4985620e2b66b45c590ed5ddda1b346b3f098",
|
||||
remote = "https://github.com/devversion/rules_angular.git",
|
||||
)
|
||||
|
||||
load("@rules_angular//setup:step_1.bzl", "rules_angular_step1")
|
||||
|
||||
rules_angular_step1()
|
||||
|
||||
load("@rules_angular//setup:step_2.bzl", "rules_angular_step2")
|
||||
|
||||
rules_angular_step2()
|
||||
|
||||
load("@rules_angular//setup:step_3.bzl", "rules_angular_step3")
|
||||
|
||||
rules_angular_step3(
|
||||
angular_compiler_cli = "@angular//tools/bazel/rules_angular_store:node_modules/@angular/compiler-cli",
|
||||
typescript = "@angular//:node_modules/typescript",
|
||||
)
|
||||
|
||||
git_repository(
|
||||
name = "rules_browsers",
|
||||
commit = "56ef8007ea07cd1916429bca8bb523433b0e9cdc",
|
||||
remote = "https://github.com/devversion/rules_browsers.git",
|
||||
)
|
||||
|
||||
load("@rules_browsers//setup:step_1.bzl", "rules_browsers_setup_1")
|
||||
|
||||
rules_browsers_setup_1()
|
||||
|
||||
load("@rules_browsers//setup:step_2.bzl", "rules_browsers_setup_2")
|
||||
|
||||
rules_browsers_setup_2()
|
||||
|
||||
http_archive(
|
||||
name = "aspect_rules_esbuild",
|
||||
sha256 = "530adfeae30bbbd097e8af845a44a04b641b680c5703b3bf885cbd384ffec779",
|
||||
strip_prefix = "rules_esbuild-0.22.1",
|
||||
url = "https://github.com/aspect-build/rules_esbuild/releases/download/v0.22.1/rules_esbuild-v0.22.1.tar.gz",
|
||||
)
|
||||
|
||||
load("@aspect_rules_esbuild//esbuild:dependencies.bzl", "rules_esbuild_dependencies")
|
||||
|
||||
rules_esbuild_dependencies()
|
||||
|
||||
load("@aspect_rules_esbuild//esbuild:repositories.bzl", "LATEST_ESBUILD_VERSION", "esbuild_register_toolchains")
|
||||
|
||||
esbuild_register_toolchains(
|
||||
name = "esbuild",
|
||||
esbuild_version = LATEST_ESBUILD_VERSION,
|
||||
)
|
||||
|
||||
# Register git toolchains
|
||||
register_toolchains(
|
||||
"@devinfra//bazel/git-toolchain:git_linux_toolchain",
|
||||
"@devinfra//bazel/git-toolchain:git_macos_x86_toolchain",
|
||||
"@devinfra//bazel/git-toolchain:git_macos_arm64_toolchain",
|
||||
"@devinfra//bazel/git-toolchain:git_windows_toolchain",
|
||||
)
|
||||
|
||||
git_repository(
|
||||
name = "rules_sass",
|
||||
commit = "cc1e845339fc45d3c8390445014d5824b85a0948",
|
||||
remote = "https://github.com/devversion/rules_sass.git",
|
||||
)
|
||||
|
||||
load("@rules_sass//src/toolchain:repositories.bzl", "setup_rules_sass")
|
||||
|
||||
setup_rules_sass()
|
||||
|
||||
http_archive(
|
||||
name = "jq.bzl",
|
||||
sha256 = "7b63435aa19cc6a0cfd1a82fbdf2c7a2f0a94db1a79ff7a4469ffa94286261ab",
|
||||
strip_prefix = "jq.bzl-0.1.0",
|
||||
url = "https://github.com/bazel-contrib/jq.bzl/releases/download/v0.1.0/jq.bzl-v0.1.0.tar.gz",
|
||||
)
|
||||
+55
-48
@@ -1,5 +1,4 @@
|
||||
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")
|
||||
load("@npm2//:defs.bzl", "npm_link_all_packages")
|
||||
load("@npm//:defs.bzl", "npm_link_all_packages")
|
||||
load("@rules_angular//src/architect:ng_application.bzl", "ng_application")
|
||||
load("@rules_angular//src/architect:ng_config.bzl", "ng_config")
|
||||
load("@rules_angular//src/architect:ng_test.bzl", "ng_test")
|
||||
@@ -63,7 +62,12 @@ APPLICATION_DEPS = [
|
||||
"//adev:node_modules/@types/node",
|
||||
"//adev:node_modules/@types/dom-navigation",
|
||||
|
||||
# Deep imports
|
||||
# The imports below are packages that our code does not directly import, but our upstream deps import from
|
||||
# these locations directly. By design, rather than hoisting these packages into the "root" node_modules the
|
||||
# dependency is found in (i.e. ./adev/node_modules), rules_js places all of each package's dependencies in
|
||||
# a "hoisted" location within the `.aspect_rules_js/` directories in the node_modules directory. This prevents
|
||||
# the bundler from being able to find it as expected. We instead specifically provide it as a dep within bazel
|
||||
# as a mechanism of "hoisting" the package for our usage by this target.
|
||||
"//adev:node_modules/@algolia/client-common",
|
||||
"//adev:node_modules/@algolia/requester-browser-xhr",
|
||||
"//adev:node_modules/@algolia/requester-node-http",
|
||||
@@ -87,60 +91,63 @@ TEST_DEPS = [
|
||||
for dep in APPLICATION_DEPS
|
||||
if dep != "//adev:node_modules/@types/node"
|
||||
] + [
|
||||
"@rules_browsers//src/browsers/chromium",
|
||||
"@rules_browsers//src/browsers/firefox",
|
||||
"@rules_browsers//browsers/chromium",
|
||||
"@rules_browsers//browsers/firefox",
|
||||
"//adev/tools:windows-chromium-path",
|
||||
]
|
||||
|
||||
ng_config(name = "ng_config")
|
||||
|
||||
bool_flag(
|
||||
name = "full_build_adev",
|
||||
build_setting_default = False,
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "prod_build",
|
||||
flag_values = {
|
||||
":full_build_adev": "true",
|
||||
},
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "dev_build",
|
||||
flag_values = {
|
||||
":full_build_adev": "false",
|
||||
},
|
||||
)
|
||||
|
||||
config_based_architect_env = select({
|
||||
":dev_build": {
|
||||
"NG_BUILD_PARTIAL_SSR": "1",
|
||||
},
|
||||
":prod_build": {
|
||||
"NG_BUILD_OPTIMIZE_CHUNKS": "1",
|
||||
},
|
||||
})
|
||||
|
||||
config_based_architect_flags = select({
|
||||
":dev_build": [
|
||||
"--configuration",
|
||||
"development",
|
||||
],
|
||||
":prod_build": [
|
||||
"--configuration",
|
||||
"production",
|
||||
],
|
||||
})
|
||||
|
||||
ng_application(
|
||||
name = "build",
|
||||
srcs = APPLICATION_FILES + APPLICATION_DEPS,
|
||||
args = config_based_architect_flags,
|
||||
env = config_based_architect_env,
|
||||
args = [
|
||||
"--configuration",
|
||||
"development",
|
||||
],
|
||||
env = {
|
||||
"NG_BUILD_PARTIAL_SSR": "1",
|
||||
},
|
||||
ng_config = ":ng_config",
|
||||
node_modules = "//adev:node_modules",
|
||||
project_name = "angular-dev",
|
||||
serve_args = [
|
||||
"--port",
|
||||
"4201",
|
||||
],
|
||||
tags = [
|
||||
# Tagged as manual as both build and build.production use the same output directory.
|
||||
"manual",
|
||||
# RBE significantly slows down the build because the CLI executes multiple CPU-intensive instances of esbuild.
|
||||
# Furthermore, the current RBE machine pool lacks high-spec machines.
|
||||
"no-remote-exec",
|
||||
],
|
||||
)
|
||||
|
||||
ng_application(
|
||||
name = "build.production",
|
||||
srcs = APPLICATION_FILES + APPLICATION_DEPS,
|
||||
args = [
|
||||
"--configuration",
|
||||
"production",
|
||||
],
|
||||
env = {
|
||||
"NG_BUILD_OPTIMIZE_CHUNKS": "1",
|
||||
},
|
||||
ng_config = ":ng_config",
|
||||
node_modules = "//adev:node_modules",
|
||||
project_name = "angular-dev",
|
||||
serve_args = [
|
||||
"--port",
|
||||
"4201",
|
||||
],
|
||||
tags = [
|
||||
# Tagged as manual as both build and build.production use the same output directory.
|
||||
"manual",
|
||||
# RBE significantly slows down the build because the CLI executes multiple CPU-intensive instances of esbuild.
|
||||
# Furthermore, the current RBE machine pool lacks high-spec machines.
|
||||
"no-remote-exec",
|
||||
],
|
||||
)
|
||||
|
||||
ng_test(
|
||||
@@ -155,7 +162,7 @@ ng_test(
|
||||
node_modules = "//adev:node_modules",
|
||||
project_name = "angular-dev",
|
||||
toolchains = [
|
||||
"@rules_browsers//src/browsers/chromium:toolchain_alias",
|
||||
"@rules_browsers//src/browsers/firefox:toolchain_alias",
|
||||
"@rules_browsers//browsers/chromium:toolchain_alias",
|
||||
"@rules_browsers//browsers/firefox:toolchain_alias",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -51,3 +51,14 @@ pnpm bazel clean
|
||||
# If that doesn't work, try it with the expunge flag
|
||||
pnpm bazel clean --expunge
|
||||
```
|
||||
|
||||
|
||||
## High level architecture
|
||||
|
||||
The angular.dev website is a modern Angular application that utilizes Static Site Generation (SSG) to deliver pre-rendered, high-performance content to users.
|
||||
|
||||
The architecture is designed for efficiency and maintainability:
|
||||
|
||||
Content Sources: The documentation is sourced from two main places within the monorepo. The guides and tutorials are written in Markdown files, while the API reference pages are automatically generated by extracting documentation directly from the TypeScript source code of the Angular framework packages.
|
||||
|
||||
Build Process: During the build process, the Markdown files are converted into HTML. Simultaneously, the API documentation is extracted from the code comments. This content is then integrated into the Angular application.
|
||||
+15
-15
@@ -1,21 +1,21 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"@angular-devkit/build-angular": "20.2.0-next.2",
|
||||
"@angular-devkit/build-angular": "20.3.2",
|
||||
"@angular/animations": "workspace:*",
|
||||
"@angular/build": "20.2.0-next.2",
|
||||
"@angular/cdk": "20.2.0-next.2",
|
||||
"@angular/cli": "20.2.0-next.2",
|
||||
"@angular/build": "20.3.2",
|
||||
"@angular/cdk": "20.2.4",
|
||||
"@angular/cli": "20.3.2",
|
||||
"@angular/common": "workspace:*",
|
||||
"@angular/compiler-cli": "workspace:*",
|
||||
"@angular/compiler": "workspace:*",
|
||||
"@angular/core": "workspace:*",
|
||||
"@angular/docs": "workspace:*",
|
||||
"@angular/forms": "workspace:*",
|
||||
"@angular/material": "20.2.0-next.2",
|
||||
"@angular/material": "20.2.4",
|
||||
"@angular/platform-browser": "workspace:*",
|
||||
"@angular/platform-server": "workspace:*",
|
||||
"@angular/router": "workspace:*",
|
||||
"@angular/ssr": "20.2.0-next.2",
|
||||
"@angular/ssr": "20.3.2",
|
||||
"@codemirror/autocomplete": "6.18.6",
|
||||
"@codemirror/commands": "6.8.1",
|
||||
"@codemirror/lang-angular": "0.1.4",
|
||||
@@ -23,7 +23,7 @@
|
||||
"@codemirror/lang-html": "6.4.9",
|
||||
"@codemirror/lang-javascript": "6.2.4",
|
||||
"@codemirror/lang-sass": "6.0.2",
|
||||
"@codemirror/language": "6.11.2",
|
||||
"@codemirror/language": "6.11.3",
|
||||
"@codemirror/lint": "6.8.5",
|
||||
"@codemirror/search": "6.5.11",
|
||||
"@codemirror/state": "6.5.2",
|
||||
@@ -33,9 +33,9 @@
|
||||
"@lezer/javascript": "1.5.1",
|
||||
"@stackblitz/sdk": "1.11.0",
|
||||
"@types/dom-navigation": "1.0.6",
|
||||
"@types/jasmine": "5.1.8",
|
||||
"@types/jasmine": "5.1.9",
|
||||
"@types/jsdom": "21.1.7",
|
||||
"@types/node": "24.1.0",
|
||||
"@types/node": "24.3.0",
|
||||
"@typescript/vfs": "1.6.1",
|
||||
"@webcontainer/api": "1.6.1",
|
||||
"@xterm/addon-fit": "0.10.0",
|
||||
@@ -52,16 +52,16 @@
|
||||
"karma-coverage": "2.2.1",
|
||||
"karma-jasmine-html-reporter": "2.1.0",
|
||||
"karma-jasmine": "5.1.0",
|
||||
"marked": "16.1.1",
|
||||
"mermaid": "11.9.0",
|
||||
"marked": "16.2.0",
|
||||
"mermaid": "11.10.0",
|
||||
"ngx-progressbar": "14.0.0",
|
||||
"open-in-idx": "0.1.1",
|
||||
"playwright-core": "1.54.1",
|
||||
"preact-render-to-string": "6.5.13",
|
||||
"preact": "10.27.0",
|
||||
"playwright-core": "1.55.0",
|
||||
"preact-render-to-string": "6.6.1",
|
||||
"preact": "10.27.1",
|
||||
"prettier": "3.6.2",
|
||||
"rxjs": "7.8.2",
|
||||
"shiki": "3.9.0",
|
||||
"shiki": "3.11.0",
|
||||
"tinyglobby": "0.2.14",
|
||||
"tslib": "2.8.1",
|
||||
"typescript": "5.9.2",
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
//tslint:disable:no-console
|
||||
import assert from 'node:assert';
|
||||
import {execSync} from 'node:child_process';
|
||||
import {existsSync, constants as fsConstants} from 'node:fs';
|
||||
import {copyFile, mkdtemp, readdir, readFile, realpath, unlink, writeFile} from 'node:fs/promises';
|
||||
@@ -19,72 +20,93 @@ export async function copyJsonAssets({repo, githubApi, assetsPath, destPath}) {
|
||||
throw new Error(`${buildInfoPath} does not exist.`);
|
||||
}
|
||||
|
||||
const branch = process.env.GITHUB_REF;
|
||||
const {sha: currentSha} = JSON.parse(await readFile(buildInfoPath, 'utf-8'));
|
||||
const latestSha = await githubApi.getShaForBranch(branch);
|
||||
assert(process.env.GITHUB_REF);
|
||||
const currentBranch = process.env.GITHUB_REF;
|
||||
|
||||
console.log(`Comparing ${currentSha}...${latestSha}.`);
|
||||
const affectedFiles = await githubApi.getAffectedFiles(currentSha, latestSha);
|
||||
const changedFiles = affectedFiles.filter((file) => file.startsWith(`${assetsPath}/`));
|
||||
|
||||
if (changedFiles.length === 0) {
|
||||
console.log(`No '${assetsPath}/**' files changed between ${currentSha} and ${latestSha}.`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`The below files changed between ${currentSha} and ${latestSha}:\n` +
|
||||
changedFiles.map((f) => '* ' + f).join('\n'),
|
||||
const {sha: storedSha, branchName: storedBranch} = JSON.parse(
|
||||
await readFile(buildInfoPath, 'utf-8'),
|
||||
);
|
||||
|
||||
const temporaryDir = await realpath(await mkdtemp(join(tmpdir(), 'copy-json-assets-')));
|
||||
const execOptions = {cwd: temporaryDir, stdio: 'inherit'};
|
||||
execSync('git init', execOptions);
|
||||
execSync(`git remote add origin https://github.com/${repo}.git`, execOptions);
|
||||
// fetch a commit
|
||||
execSync(`git fetch origin ${latestSha}`, execOptions);
|
||||
// reset this repository's main branch to the commit of interest
|
||||
execSync('git reset --hard FETCH_HEAD', execOptions);
|
||||
// get sha when files where changed
|
||||
const shaWhenFilesChanged = execSync(`git rev-list -1 ${latestSha} "${assetsPath}/"`, {
|
||||
encoding: 'utf8',
|
||||
cwd: temporaryDir,
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
}).trim();
|
||||
let downstreamBranch = currentBranch;
|
||||
let latestSha = await githubApi.getShaForBranch(currentBranch);
|
||||
if (
|
||||
latestSha.includes('No commit') &&
|
||||
currentBranch !== 'refs/heads/main' &&
|
||||
currentBranch !== storedBranch
|
||||
) {
|
||||
// In some cases, such as when a new branch is created for a feature,
|
||||
// the branch may not exist in the downstream repo. For example, during an
|
||||
// exceptional minor release (e.g. FW 20.3.x and Components: 20.2.x).
|
||||
// In such scenarios, we fallback to the last known branch.
|
||||
latestSha = await githubApi.getShaForBranch(storedBranch);
|
||||
downstreamBranch = storedBranch;
|
||||
}
|
||||
|
||||
// Delete existing asset files.
|
||||
const apiFilesUnlink = (await readdir(destPath))
|
||||
.filter((f) => f.endsWith('.json'))
|
||||
.map((f) => unlink(join(destPath, f)));
|
||||
console.log(`Comparing ${storedSha}...${latestSha}.`);
|
||||
const affectedFiles = await githubApi.getAffectedFiles(storedSha, latestSha);
|
||||
const changedFiles = affectedFiles.filter((file) => file.startsWith(`${assetsPath}/`));
|
||||
|
||||
await Promise.allSettled(apiFilesUnlink);
|
||||
let shaWhenFilesChanged;
|
||||
if (changedFiles.length > 0) {
|
||||
console.log(
|
||||
`The below files changed between ${storedSha} and ${latestSha}:\n` +
|
||||
changedFiles.map((f) => '* ' + f).join('\n'),
|
||||
);
|
||||
|
||||
// Copy new asset files
|
||||
const tempAssetsDir = join(temporaryDir, assetsPath);
|
||||
const assetFilesCopy = (await readdir(tempAssetsDir)).map((f) => {
|
||||
const src = join(tempAssetsDir, f);
|
||||
const dest = join(destPath, f);
|
||||
const temporaryDir = await realpath(await mkdtemp(join(tmpdir(), 'copy-json-assets-')));
|
||||
const execOptions = {cwd: temporaryDir, stdio: 'inherit'};
|
||||
execSync('git init', execOptions);
|
||||
execSync(`git remote add origin https://github.com/${repo}.git`, execOptions);
|
||||
// fetch a commit
|
||||
execSync(`git fetch origin ${latestSha}`, execOptions);
|
||||
// reset this repository's main branch to the commit of interest
|
||||
execSync('git reset --hard FETCH_HEAD', execOptions);
|
||||
// get sha when files where changed
|
||||
shaWhenFilesChanged = execSync(`git rev-list -1 ${latestSha} "${assetsPath}/"`, {
|
||||
encoding: 'utf8',
|
||||
cwd: temporaryDir,
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
}).trim();
|
||||
|
||||
return copyFile(src, dest, fsConstants.COPYFILE_FICLONE);
|
||||
});
|
||||
// Delete existing asset files.
|
||||
const apiFilesUnlink = (await readdir(destPath))
|
||||
.filter((f) => f.endsWith('.json'))
|
||||
.map((f) => unlink(join(destPath, f)));
|
||||
|
||||
await Promise.allSettled(assetFilesCopy);
|
||||
await Promise.allSettled(apiFilesUnlink);
|
||||
|
||||
// Copy new asset files
|
||||
const tempAssetsDir = join(temporaryDir, assetsPath);
|
||||
const assetFilesCopy = (await readdir(tempAssetsDir)).map((f) => {
|
||||
const src = join(tempAssetsDir, f);
|
||||
const dest = join(destPath, f);
|
||||
|
||||
return copyFile(src, dest, fsConstants.COPYFILE_FICLONE);
|
||||
});
|
||||
|
||||
await Promise.allSettled(assetFilesCopy);
|
||||
|
||||
console.log(`Successfully updated asset files in '${destPath}'.\n`);
|
||||
} else {
|
||||
console.log(`No '${assetsPath}/**' files changed between ${storedSha} and ${latestSha}.`);
|
||||
}
|
||||
|
||||
// Write SHA to file.
|
||||
await writeFile(
|
||||
buildInfoPath,
|
||||
JSON.stringify(
|
||||
{
|
||||
branchName: branch,
|
||||
sha: shaWhenFilesChanged,
|
||||
branchName: downstreamBranch,
|
||||
sha: shaWhenFilesChanged ?? storedSha,
|
||||
},
|
||||
undefined,
|
||||
2,
|
||||
),
|
||||
);
|
||||
|
||||
// The below command will show uncommitted changes.
|
||||
// This is expected because the framework repo and component/cli might have different minors
|
||||
// (e.g. during exceptional minor releases), leading to changes that need to be committed.
|
||||
console.log('\nChanges: ');
|
||||
execSync(`git status --porcelain`, {stdio: 'inherit'});
|
||||
|
||||
console.log(`Successfully updated asset files in '${destPath}'.\n`);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ import {RouterLink} from '@angular/router';
|
||||
export class Breadcrumb {
|
||||
private readonly navigationState = inject(NavigationState);
|
||||
|
||||
breadcrumbItems = computed(() => {
|
||||
readonly breadcrumbItems = computed(() => {
|
||||
const breadcrumbs: NavigationItem[] = [];
|
||||
let activeItem = this.navigationState.activeNavigationItem()?.parent;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
<div>
|
||||
<a href="https://policies.google.com/technologies/cookies" target="_blank" rel="noopener">
|
||||
<button class="docs-primary-btn" [attr.text]="'Learn more'" aria-label="Learn More">
|
||||
<button type="button" class="docs-primary-btn" [attr.text]="'Learn more'" aria-label="Learn More">
|
||||
Learn more
|
||||
</button>
|
||||
</a>
|
||||
|
||||
@@ -11,7 +11,7 @@ import {LOCAL_STORAGE} from '../../providers/index';
|
||||
import {setCookieConsent} from '../../utils';
|
||||
|
||||
/**
|
||||
* Decelare gtag as part of the window in this file as gtag is expected to already be loaded.
|
||||
* Declare gtag as part of the window in this file as gtag is expected to already be loaded.
|
||||
*/
|
||||
declare const window: Window & typeof globalThis & {gtag?: Function};
|
||||
|
||||
@@ -27,7 +27,7 @@ export class CookiePopup {
|
||||
private readonly localStorage = inject(LOCAL_STORAGE);
|
||||
|
||||
/** Whether the user has accepted the cookie disclaimer. */
|
||||
hasAccepted = signal<boolean>(false);
|
||||
readonly hasAccepted = signal<boolean>(false);
|
||||
|
||||
constructor() {
|
||||
// Needs to be in a try/catch, because some browsers will
|
||||
|
||||
+1
-2
@@ -15,7 +15,6 @@ import {
|
||||
inject,
|
||||
signal,
|
||||
} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Clipboard} from '@angular/cdk/clipboard';
|
||||
import {IconComponent} from '../icon/icon.component';
|
||||
|
||||
@@ -24,7 +23,7 @@ export const CONFIRMATION_DISPLAY_TIME_MS = 2000;
|
||||
|
||||
@Component({
|
||||
selector: 'button[docs-copy-source-code]',
|
||||
imports: [CommonModule, IconComponent],
|
||||
imports: [IconComponent],
|
||||
templateUrl: './copy-source-code-button.component.html',
|
||||
host: {
|
||||
'type': 'button',
|
||||
|
||||
@@ -20,6 +20,7 @@ ng_project(
|
||||
"//adev:node_modules/@angular/material",
|
||||
"//adev:node_modules/@angular/router",
|
||||
"//adev/shared-docs/components/icon",
|
||||
"//adev/shared-docs/components/slide-toggle",
|
||||
"//adev/shared-docs/interfaces",
|
||||
"//adev/shared-docs/pipes",
|
||||
"//adev/shared-docs/services",
|
||||
|
||||
@@ -3,106 +3,115 @@
|
||||
class="docs-navigation-list docs-faceted-list"
|
||||
[class.docs-navigation-list-dropdown]="isDropdownView()"
|
||||
>
|
||||
@for (item of navigationItems; track $index) {
|
||||
<li class="docs-faceted-list-item">
|
||||
@if (item.path) {
|
||||
@if (item.isExternal) {
|
||||
<a
|
||||
[href]="item.path"
|
||||
target="_blank"
|
||||
[matTooltip]="item.label"
|
||||
[matTooltipDisabled]="item.label.length < 27"
|
||||
matTooltipPosition="after"
|
||||
[attr.aria-label]="item.label"
|
||||
[matTooltipClass]="'API-tooltip'"
|
||||
>
|
||||
<span
|
||||
[class.docs-external-link]="item.isExternal"
|
||||
class="docs-faceted-list-item-text"
|
||||
@for (itemGroup of groupItems(navigationItems); track $index) {
|
||||
@let groupLabel = itemGroup[0];
|
||||
@let items = itemGroup[1];
|
||||
@if (groupLabel) {
|
||||
<li class="docs-navigation-group">{{groupLabel | titlecase}}</li>
|
||||
}
|
||||
@for (item of items; track $index) {
|
||||
<li class="docs-faceted-list-item">
|
||||
@if (item.path) {
|
||||
@if (item.isExternal) {
|
||||
<a
|
||||
[href]="item.path"
|
||||
target="_blank"
|
||||
[matTooltip]="item.label"
|
||||
[matTooltipDisabled]="item.label.length < 27"
|
||||
matTooltipPosition="after"
|
||||
[attr.aria-label]="item.label"
|
||||
[matTooltipClass]="'API-tooltip'"
|
||||
>
|
||||
{{ item.label }}
|
||||
<ng-container *ngTemplateOutlet="itemStatus; context: {$implicit: item}" />
|
||||
</span>
|
||||
<span
|
||||
[class.docs-external-link]="item.isExternal"
|
||||
class="docs-faceted-list-item-text"
|
||||
>
|
||||
{{ item.label }}
|
||||
<ng-container *ngTemplateOutlet="itemStatus; context: {$implicit: item}" />
|
||||
</span>
|
||||
|
||||
@if (item.children && item.level! > 1 && !item.isExpanded) {
|
||||
<docs-icon>chevron_right</docs-icon>
|
||||
}
|
||||
</a>
|
||||
@if (item.children && item.level! > 1 && !item.isExpanded) {
|
||||
<docs-icon>chevron_right</docs-icon>
|
||||
}
|
||||
</a>
|
||||
} @else {
|
||||
<a
|
||||
[routerLink]="'/' + item.path"
|
||||
routerLinkActive="docs-faceted-list-item-active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
(click)="emitClickOnLink()"
|
||||
[matTooltip]="item.label"
|
||||
[matTooltipDisabled]="item.label.length < 27"
|
||||
matTooltipPosition="after"
|
||||
[attr.aria-label]="item.label"
|
||||
[matTooltipClass]="'API-tooltip'"
|
||||
>
|
||||
<span class="docs-faceted-list-item-text">
|
||||
{{item.label}}
|
||||
<ng-container *ngTemplateOutlet="itemStatus; context: {$implicit: item}" />
|
||||
</span>
|
||||
|
||||
@if (item.children && !item.isExpanded) {
|
||||
<docs-icon>chevron_right</docs-icon>
|
||||
}
|
||||
</a>
|
||||
}
|
||||
} @else {
|
||||
<a
|
||||
[routerLink]="'/' + item.path"
|
||||
routerLinkActive="docs-faceted-list-item-active"
|
||||
[routerLinkActiveOptions]="{ exact: true }"
|
||||
(click)="emitClickOnLink()"
|
||||
[matTooltip]="item.label"
|
||||
[matTooltipDisabled]="item.label.length < 27"
|
||||
matTooltipPosition="after"
|
||||
[attr.aria-label]="item.label"
|
||||
[matTooltipClass]="'API-tooltip'"
|
||||
>
|
||||
<span class="docs-faceted-list-item-text">
|
||||
{{item.label}}
|
||||
<ng-container *ngTemplateOutlet="itemStatus; context: {$implicit: item}" />
|
||||
</span>
|
||||
<!-- Nav Section Header -->
|
||||
@if (item.level !== collapsableLevel() && item.level !== expandableLevel()) {
|
||||
<div
|
||||
class="docs-secondary-nav-header"
|
||||
[matTooltip]="item.label"
|
||||
[matTooltipDisabled]="item.label.length < 27"
|
||||
matTooltipPosition="after"
|
||||
[attr.aria-label]="item.label"
|
||||
[matTooltipClass]="'API-tooltip'"
|
||||
>
|
||||
<span class="docs-faceted-list-item-text">
|
||||
{{item.label}}
|
||||
<ng-container *ngTemplateOutlet="itemStatus; context: {$implicit: item}" />
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (item.children && !item.isExpanded) {
|
||||
<docs-icon>chevron_right</docs-icon>
|
||||
}
|
||||
</a>
|
||||
}
|
||||
} @else {
|
||||
<!-- Nav Section Header -->
|
||||
@if (item.level !== collapsableLevel() && item.level !== expandableLevel()) {
|
||||
<div
|
||||
class="docs-secondary-nav-header"
|
||||
[matTooltip]="item.label"
|
||||
[matTooltipDisabled]="item.label.length < 27"
|
||||
matTooltipPosition="after"
|
||||
[attr.aria-label]="item.label"
|
||||
[matTooltipClass]="'API-tooltip'"
|
||||
>
|
||||
<span class="docs-faceted-list-item-text">
|
||||
{{item.label}}
|
||||
<ng-container *ngTemplateOutlet="itemStatus; context: {$implicit: item}" />
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Nav Button Expand/Collapse -->
|
||||
@if (
|
||||
(item.children && item.level === expandableLevel()) || item.level === collapsableLevel()
|
||||
) {
|
||||
<button
|
||||
type="button"
|
||||
(click)="toggle(item)"
|
||||
[attr.aria-label]="(item.isExpanded ? 'Collapse' : 'Expand') + ' ' + item.label"
|
||||
[attr.aria-expanded]="item.isExpanded"
|
||||
class="docs-secondary-nav-button"
|
||||
[class.docs-faceted-list-item-active]="item | isActiveNavigationItem: activeItem()"
|
||||
[class.docs-expanded-button]="item.children && item.level == collapsableLevel()"
|
||||
[class.docs-not-expanded-button]="item.children && item.level === expandableLevel()"
|
||||
[class.docs-nav-item-has-icon]="
|
||||
<!-- Nav Button Expand/Collapse -->
|
||||
@if (
|
||||
(item.children && item.level === expandableLevel()) || item.level === collapsableLevel()
|
||||
) {
|
||||
<button
|
||||
type="button"
|
||||
(click)="toggle(item)"
|
||||
[attr.aria-label]="(item.isExpanded ? 'Collapse' : 'Expand') + ' ' + item.label"
|
||||
[attr.aria-expanded]="item.isExpanded"
|
||||
class="docs-secondary-nav-button"
|
||||
[class.docs-faceted-list-item-active]="item | isActiveNavigationItem: activeItem()"
|
||||
[class.docs-expanded-button]="item.children && item.level == collapsableLevel()"
|
||||
[class.docs-not-expanded-button]="item.children && item.level === expandableLevel()"
|
||||
[class.docs-nav-item-has-icon]="
|
||||
item.children && item.level === expandableLevel() && !item.isExpanded"
|
||||
[matTooltip]="item.label"
|
||||
[matTooltipDisabled]="item.label.length < 27"
|
||||
matTooltipPosition="after"
|
||||
[attr.aria-label]="item.label"
|
||||
[matTooltipClass]="'API-tooltip'"
|
||||
>
|
||||
@if (item.children && item.level === collapsableLevel()) {
|
||||
<docs-icon>arrow_back</docs-icon>
|
||||
}
|
||||
<span class="docs-faceted-list-item-text">
|
||||
{{ item.label }}
|
||||
<ng-container *ngTemplateOutlet="itemStatus; context: {$implicit: item}" />
|
||||
</span>
|
||||
</button>
|
||||
[matTooltip]="item.label"
|
||||
[matTooltipDisabled]="item.label.length < 27"
|
||||
matTooltipPosition="after"
|
||||
[attr.aria-label]="item.label"
|
||||
[matTooltipClass]="'API-tooltip'"
|
||||
>
|
||||
@if (item.children && item.level === collapsableLevel()) {
|
||||
<docs-icon>arrow_back</docs-icon>
|
||||
}
|
||||
<span class="docs-faceted-list-item-text">
|
||||
{{ item.label }}
|
||||
<ng-container *ngTemplateOutlet="itemStatus; context: {$implicit: item}" />
|
||||
</span>
|
||||
</button>
|
||||
}
|
||||
}
|
||||
}
|
||||
@if (displayItemsToLevel() > item.level && item.children?.length > 0) {
|
||||
<ng-container *ngTemplateOutlet="navigationList; context: {$implicit: item.children}" />
|
||||
}
|
||||
</li>
|
||||
@if (displayItemsToLevel() > item.level && item.children?.length > 0) {
|
||||
<ng-container
|
||||
*ngTemplateOutlet="navigationList; context: {$implicit: item.children, level: item.level}"
|
||||
/>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
</ng-template>
|
||||
|
||||
@@ -185,3 +185,11 @@ a,
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
|
||||
.docs-navigation-group {
|
||||
padding: 0.5rem 0.5rem 0.5rem 1rem;
|
||||
background: var(--septenary-contrast);
|
||||
&:not(:first-child) {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,11 @@ import {NavigationItem} from '../../interfaces/index';
|
||||
import {NavigationState} from '../../services/index';
|
||||
import {RouterLink, RouterLinkActive} from '@angular/router';
|
||||
import {IconComponent} from '../icon/icon.component';
|
||||
import {IsActiveNavigationItem} from '../../pipes/is-active-navigation-item.pipe';
|
||||
import {NgTemplateOutlet} from '@angular/common';
|
||||
import {IsActiveNavigationItem} from '../../pipes';
|
||||
import {NgTemplateOutlet, TitleCasePipe} from '@angular/common';
|
||||
import {MatTooltipModule} from '@angular/material/tooltip';
|
||||
import {SlideToggle} from '../slide-toggle/slide-toggle.component';
|
||||
import {FormControl, ReactiveFormsModule} from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'docs-navigation-list',
|
||||
@@ -24,6 +26,8 @@ import {MatTooltipModule} from '@angular/material/tooltip';
|
||||
IsActiveNavigationItem,
|
||||
NgTemplateOutlet,
|
||||
MatTooltipModule,
|
||||
ReactiveFormsModule,
|
||||
TitleCasePipe,
|
||||
],
|
||||
templateUrl: './navigation-list.component.html',
|
||||
styleUrls: ['./navigation-list.component.scss'],
|
||||
@@ -56,4 +60,33 @@ export class NavigationList {
|
||||
emitClickOnLink(): void {
|
||||
this.linkClicked.emit();
|
||||
}
|
||||
|
||||
private hasCategories(items: NavigationItem[]): boolean {
|
||||
return items.some((item) => !!item.category);
|
||||
}
|
||||
|
||||
protected groupItems(items: NavigationItem[]): Map<string, NavigationItem[]> {
|
||||
const hasCategories = this.hasCategories(items);
|
||||
if (hasCategories) {
|
||||
const others: NavigationItem[] = [];
|
||||
const categorizedItems = new Map<string, NavigationItem[]>();
|
||||
for (const item of items) {
|
||||
const category = item.category || 'Other';
|
||||
if (category === 'Other') {
|
||||
others.push(item);
|
||||
continue;
|
||||
}
|
||||
if (!categorizedItems.has(category)) {
|
||||
categorizedItems.set(category, []);
|
||||
}
|
||||
categorizedItems.get(category)!.push(item);
|
||||
}
|
||||
if (others.length) {
|
||||
categorizedItems.set('Other', others);
|
||||
}
|
||||
return categorizedItems;
|
||||
} else {
|
||||
return new Map([['', items]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ ng_project(
|
||||
"//adev/shared-docs/directives",
|
||||
"//adev/shared-docs/interfaces",
|
||||
"//adev/shared-docs/pipes",
|
||||
"//adev/shared-docs/providers",
|
||||
"//adev/shared-docs/services",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -12,35 +12,49 @@
|
||||
@if (searchResults().length) {
|
||||
<ul class="docs-search-results docs-mini-scroll-track">
|
||||
@for (result of searchResults(); track result.id) {
|
||||
<li docsSearchItem [item]="result">
|
||||
<li docsSearchItem [item]="result" class="docs-search-result">
|
||||
<a
|
||||
[routerLink]="'/' + result.url | relativeLink: 'pathname'"
|
||||
[fragment]="result.url | relativeLink: 'hash'"
|
||||
(click)="history.addItem(result)"
|
||||
>
|
||||
<div>
|
||||
<div class="docs-result-icon-and-type">
|
||||
<p class="docs-search-result__label">
|
||||
<!-- Icon -->
|
||||
<span class="docs-search-result-icon" aria-hidden="true">
|
||||
<i role="presentation" class="material-symbols-outlined docs-icon-small">
|
||||
{{ result.type === 'code' ? 'code' : 'description'}}
|
||||
</i>
|
||||
</span>
|
||||
<!-- Results type -->
|
||||
<span class="docs-search-results__type" [innerHtml]="result.labelHtml"></span>
|
||||
</div>
|
||||
|
||||
<!-- Page title -->
|
||||
<span [innerHtml]="result.labelHtml"></span>
|
||||
@if (result.package) {
|
||||
<span
|
||||
[innerHTML]="result.package"
|
||||
class="docs-search-result__label__package"
|
||||
></span>
|
||||
}
|
||||
</p>
|
||||
|
||||
@if (result.subLabelHtml) {
|
||||
<span
|
||||
class="docs-search-results__type docs-search-results__lvl2"
|
||||
[innerHtml]="result.subLabelHtml"
|
||||
>
|
||||
</span>
|
||||
<p class="docs-search-result__sub-label">
|
||||
<span class="docs-search-result-icon" aria-hidden="true">
|
||||
<i role="presentation" class="material-symbols-outlined docs-icon-small">
|
||||
grid_3x3
|
||||
</i>
|
||||
</span>
|
||||
<span [innerHtml]="result.subLabelHtml"></span>
|
||||
</p>
|
||||
}
|
||||
|
||||
@if (result.contentHtml) {
|
||||
<p class="docs-search-result__content" [innerHtml]="result.contentHtml"></p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Page title -->
|
||||
<span class="docs-result-page-title">{{ result.category }}</span>
|
||||
<p class="docs-search-result__category">
|
||||
{{ result.category }}
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
|
||||
@@ -38,11 +38,21 @@ dialog {
|
||||
margin: 0;
|
||||
border-block-end: 1px solid var(--senary-contrast);
|
||||
|
||||
li {
|
||||
.docs-search-result {
|
||||
border-inline-start: 2px solid var(--senary-contrast);
|
||||
margin-inline-start: 1rem;
|
||||
padding-inline-end: 1rem;
|
||||
padding-block: 0.25rem;
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
|
||||
.docs-search-result-icon {
|
||||
display: inline-block;
|
||||
|
||||
i {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This rule needs ng-deep to be applied to elements that are created via a [innerHTML] binding
|
||||
@@ -61,17 +71,50 @@ dialog {
|
||||
color: var(--secondary-contrast);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 1rem;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.docs-search-result-icon {
|
||||
i {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
&__label,
|
||||
&__sub-label,
|
||||
&__content {
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
|
||||
&__label,
|
||||
&__sub-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__label {
|
||||
font-weight: 600;
|
||||
flex-wrap: wrap;
|
||||
|
||||
&__package {
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__content,
|
||||
&__category {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&__sub-label,
|
||||
&__content {
|
||||
margin-block-start: 0.375rem;
|
||||
margin-inline-start: 2rem;
|
||||
color: var(--quaternary-contrast);
|
||||
}
|
||||
|
||||
&__category {
|
||||
font-weight: 400;
|
||||
color: var(--quaternary-contrast);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: var(--septenary-contrast); // stylelint-disable-line
|
||||
}
|
||||
@@ -80,44 +123,14 @@ dialog {
|
||||
&.active {
|
||||
background-color: var(--octonary-contrast); // stylelint-disable-line
|
||||
border-inline-start: 2px solid var(--primary-contrast);
|
||||
a {
|
||||
span:not(.docs-result-page-title),
|
||||
.docs-search-results__type {
|
||||
color: var(--primary-contrast);
|
||||
i {
|
||||
color: var(--primary-contrast);
|
||||
}
|
||||
}
|
||||
|
||||
.docs-search-result__label,
|
||||
.docs-search-result__sub-label,
|
||||
.docs-search-result__content {
|
||||
color: var(--primary-contrast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.docs-search-result-icon,
|
||||
.docs-search-results__type,
|
||||
.docs-result-page-title {
|
||||
color: var(--quaternary-contrast);
|
||||
display: inline-block;
|
||||
font-size: 0.875rem;
|
||||
transition: color 0.3s ease;
|
||||
padding: 0.75rem;
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
|
||||
.docs-search-results__lvl2 {
|
||||
display: inline-block;
|
||||
margin-inline-start: 2rem;
|
||||
padding-block-start: 0;
|
||||
}
|
||||
|
||||
.docs-search-results__lvl3 {
|
||||
margin-inline-start: 2rem;
|
||||
padding-block-start: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.docs-result-page-title {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,14 +140,6 @@ dialog {
|
||||
color: var(--gray-400);
|
||||
}
|
||||
|
||||
.docs-result-icon-and-type {
|
||||
display: flex;
|
||||
|
||||
.docs-search-results__type {
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.docs-search-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -20,19 +20,18 @@ import {
|
||||
viewChildren,
|
||||
} from '@angular/core';
|
||||
|
||||
import {WINDOW} from '../../providers/index';
|
||||
import {ClickOutside} from '../../directives/index';
|
||||
import {Search, SearchHistory} from '../../services/index';
|
||||
import {WINDOW} from '../../providers';
|
||||
import {ClickOutside, SearchItem} from '../../directives';
|
||||
import {Search, SearchHistory} from '../../services';
|
||||
|
||||
import {TextField} from '../text-field/text-field.component';
|
||||
import {FormControl, ReactiveFormsModule} from '@angular/forms';
|
||||
import {ActiveDescendantKeyManager} from '@angular/cdk/a11y';
|
||||
import {SearchItem} from '../../directives/search-item/search-item.directive';
|
||||
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||
import {Router, RouterLink} from '@angular/router';
|
||||
import {fromEvent} from 'rxjs';
|
||||
import {AlgoliaIcon} from '../algolia-icon/algolia-icon.component';
|
||||
import {RelativeLink} from '../../pipes/relative-link.pipe';
|
||||
import {RelativeLink} from '../../pipes';
|
||||
import {SearchHistoryComponent} from '../search-history/search-history.component';
|
||||
|
||||
@Component({
|
||||
@@ -52,10 +51,10 @@ import {SearchHistoryComponent} from '../search-history/search-history.component
|
||||
styleUrls: ['./search-dialog.component.scss'],
|
||||
})
|
||||
export class SearchDialog {
|
||||
onClose = output();
|
||||
dialog = viewChild.required<ElementRef<HTMLDialogElement>>('searchDialog');
|
||||
items = viewChildren(SearchItem);
|
||||
textField = viewChild(TextField);
|
||||
readonly onClose = output();
|
||||
readonly dialog = viewChild.required<ElementRef<HTMLDialogElement>>('searchDialog');
|
||||
readonly items = viewChildren(SearchItem);
|
||||
readonly textField = viewChild(TextField);
|
||||
|
||||
readonly history = inject(SearchHistory);
|
||||
private readonly search = inject(Search);
|
||||
@@ -68,9 +67,9 @@ export class SearchDialog {
|
||||
this.injector,
|
||||
).withWrap();
|
||||
|
||||
searchQuery = this.search.searchQuery;
|
||||
resultsResource = this.search.resultsResource;
|
||||
searchResults = this.search.searchResults;
|
||||
readonly searchQuery = this.search.searchQuery;
|
||||
readonly resultsResource = this.search.resultsResource;
|
||||
readonly searchResults = this.search.searchResults;
|
||||
|
||||
// We use a FormControl instead of relying on NgModel+signal to avoid
|
||||
// the issue https://github.com/angular/angular/issues/13568
|
||||
|
||||
@@ -22,6 +22,7 @@ ng_project(
|
||||
],
|
||||
deps = [
|
||||
"//adev:node_modules/@angular/cdk",
|
||||
"//adev:node_modules/@angular/common",
|
||||
"//adev:node_modules/@angular/core",
|
||||
"//adev:node_modules/@angular/router",
|
||||
"//adev/shared-docs/directives",
|
||||
@@ -38,6 +39,8 @@ ts_project(
|
||||
":search-history",
|
||||
"//adev:node_modules/@angular/core",
|
||||
"//adev:node_modules/@angular/platform-browser",
|
||||
"//adev:node_modules/@angular/router",
|
||||
"//adev/shared-docs/providers",
|
||||
"//adev/shared-docs/services",
|
||||
"//adev/shared-docs/testing",
|
||||
],
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
<ng-template #searchItem let-item="item" let-type="type">
|
||||
<i role="presentation" class="material-symbols-outlined type-icon" aria-hidden="true">
|
||||
{{ type === 'recent' ? 'history' : 'star' }}
|
||||
</i>
|
||||
<span [innerHTML]="item.labelHtml" class="label"></span>
|
||||
|
||||
@if (item.subLabelHtml) {
|
||||
<i role="presentation" class="material-symbols-outlined" aria-hidden="true"> arrow_right </i>
|
||||
<span [innerHTML]="item.subLabelHtml" class="sub-label"></span>
|
||||
}
|
||||
</ng-template>
|
||||
|
||||
@let items = history.items();
|
||||
|
||||
@if (items.recent.length) {
|
||||
@@ -10,8 +22,10 @@
|
||||
[fragment]="item.url | relativeLink: 'hash'"
|
||||
(click)="history.addItem(item)"
|
||||
>
|
||||
<i role="presentation" class="material-symbols-outlined" aria-hidden="true"> history </i>
|
||||
<span [innerHTML]="item.labelHtml"></span>
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="searchItem"
|
||||
[ngTemplateOutletContext]="{item, type: 'recent'}"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<button
|
||||
@@ -20,7 +34,9 @@
|
||||
(click)="history.makeFavorite(item)"
|
||||
title="Make favorite"
|
||||
>
|
||||
<i role="presentation" class="material-symbols-outlined" aria-hidden="true"> star </i>
|
||||
<i role="presentation" class="material-symbols-outlined type-icon" aria-hidden="true">
|
||||
star
|
||||
</i>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@@ -48,8 +64,10 @@
|
||||
[routerLink]="'/' + item.url | relativeLink: 'pathname'"
|
||||
[fragment]="item.url | relativeLink: 'hash'"
|
||||
>
|
||||
<i role="presentation" class="material-symbols-outlined" aria-hidden="true"> star </i>
|
||||
<span [innerHTML]="item.labelHtml"></span>
|
||||
<ng-container
|
||||
[ngTemplateOutlet]="searchItem"
|
||||
[ngTemplateOutletContext]="{item, type: 'favorite'}"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<button
|
||||
|
||||
@@ -41,9 +41,23 @@
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
color: var(--secondary-contrast);
|
||||
transition: color 0.3s ease;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
.type-icon {
|
||||
margin-inline-end: 0.75rem;
|
||||
}
|
||||
|
||||
span {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
|
||||
&.sub-label {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--primary-contrast);
|
||||
|
||||
@@ -18,14 +18,15 @@ import {
|
||||
import {Router, RouterLink} from '@angular/router';
|
||||
import {toSignal} from '@angular/core/rxjs-interop';
|
||||
import {ActiveDescendantKeyManager} from '@angular/cdk/a11y';
|
||||
import {NgTemplateOutlet} from '@angular/common';
|
||||
|
||||
import {SearchHistory} from '../../services';
|
||||
import {RelativeLink} from '../../pipes';
|
||||
import {SearchItem} from '../../directives/search-item/search-item.directive';
|
||||
import {SearchItem} from '../../directives';
|
||||
|
||||
@Component({
|
||||
selector: 'docs-search-history',
|
||||
imports: [RelativeLink, RouterLink, SearchItem],
|
||||
imports: [NgTemplateOutlet, RelativeLink, RouterLink, SearchItem],
|
||||
templateUrl: './search-history.component.html',
|
||||
styleUrl: './search-history.component.scss',
|
||||
host: {
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR, FormsModule} from '@angular/forms';
|
||||
import {ChangeDetectionStrategy, Component, model, forwardRef, input, signal} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
|
||||
type SelectOptionValue = string | number | boolean;
|
||||
|
||||
@@ -20,7 +19,7 @@ export interface SelectOption {
|
||||
@Component({
|
||||
selector: 'docs-select',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [CommonModule, FormsModule],
|
||||
imports: [FormsModule],
|
||||
templateUrl: './select.component.html',
|
||||
styleUrls: ['./select.component.scss'],
|
||||
providers: [
|
||||
|
||||
@@ -13,6 +13,7 @@ ng_project(
|
||||
],
|
||||
visibility = [
|
||||
"//adev/shared-docs/components:__pkg__",
|
||||
"//adev/shared-docs/components/navigation-list:__pkg__",
|
||||
],
|
||||
deps = [
|
||||
"//adev:node_modules/@angular/common",
|
||||
|
||||
@@ -7,12 +7,11 @@
|
||||
*/
|
||||
|
||||
import {ChangeDetectionStrategy, Component, forwardRef, model, input, signal} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'docs-slide-toggle',
|
||||
imports: [CommonModule],
|
||||
imports: [],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
templateUrl: './slide-toggle.component.html',
|
||||
styleUrls: ['./slide-toggle.component.scss'],
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
} from '@angular/core';
|
||||
import {Location, ViewportScroller} from '@angular/common';
|
||||
import {TableOfContentsLevel} from '../../interfaces/index';
|
||||
import {TableOfContentsLoader} from '../../services/table-of-contents-loader.service';
|
||||
import {TableOfContentsLoader} from '../../services';
|
||||
import {IconComponent} from '../icon/icon.component';
|
||||
|
||||
@Component({
|
||||
@@ -38,7 +38,7 @@ export class TableOfContents {
|
||||
|
||||
tableOfContentItems = this.tableOfContentsLoader.tableOfContentItems;
|
||||
|
||||
activeItemId = signal<string | null>(null);
|
||||
readonly activeItemId = signal<string | null>(null);
|
||||
TableOfContentsLevel = TableOfContentsLevel;
|
||||
|
||||
constructor() {
|
||||
|
||||
@@ -17,14 +17,13 @@ import {
|
||||
model,
|
||||
viewChild,
|
||||
} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR} from '@angular/forms';
|
||||
import {IconComponent} from '../icon/icon.component';
|
||||
|
||||
@Component({
|
||||
selector: 'docs-text-field',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [CommonModule, FormsModule, IconComponent],
|
||||
imports: [FormsModule, IconComponent],
|
||||
templateUrl: './text-field.component.html',
|
||||
styleUrls: ['./text-field.component.scss'],
|
||||
providers: [
|
||||
|
||||
@@ -29,16 +29,16 @@ export class TopLevelBannerComponent {
|
||||
* separately for different events or instances. Without a unique ID,
|
||||
* closing one banner could inadvertently hide other banners for different events.
|
||||
*/
|
||||
id = input.required<string>();
|
||||
readonly id = input.required<string>();
|
||||
// Optional URL link that the banner should navigate to when clicked.
|
||||
link = input<string>();
|
||||
readonly link = input<string>();
|
||||
// Text content to be displayed in the banner.
|
||||
text = input.required<string>();
|
||||
readonly text = input.required<string>();
|
||||
// Optional expiry date. Setting the default expiry as a future date so we
|
||||
// don't have to deal with undefined signal values.
|
||||
expiry = input(new Date('3000-01-01'), {transform: parseDate});
|
||||
readonly expiry = input(new Date('3000-01-01'), {transform: parseDate});
|
||||
// Whether the user has closed the banner or the survey has expired.
|
||||
hasClosed = linkedSignal(() => {
|
||||
readonly hasClosed = linkedSignal(() => {
|
||||
const expired = Date.now() > this.expiry().getTime();
|
||||
|
||||
// Needs to be in a try/catch, because some browsers will
|
||||
|
||||
@@ -21,13 +21,17 @@ ng_project(
|
||||
"//adev:node_modules/@angular/common",
|
||||
"//adev:node_modules/@angular/core",
|
||||
"//adev:node_modules/@angular/material",
|
||||
"//adev:node_modules/@angular/platform-browser",
|
||||
"//adev:node_modules/@angular/router",
|
||||
"//adev:node_modules/rxjs",
|
||||
"//adev/shared-docs/components/breadcrumb",
|
||||
"//adev/shared-docs/components/copy-source-code-button",
|
||||
"//adev/shared-docs/components/icon",
|
||||
"//adev/shared-docs/components/table-of-contents",
|
||||
"//adev/shared-docs/interfaces",
|
||||
"//adev/shared-docs/providers",
|
||||
"//adev/shared-docs/services",
|
||||
"//adev/shared-docs/utils",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
// $screen-xl is the breakpoint where Main content stop being fluid and made fixed
|
||||
|
||||
>*:not(docs-table-of-contents) {
|
||||
max-width: calc(mq.$screen-xxl - var(--secondary-nav-width) - var(--primary-nav-width) - var(--layout-padding)*2)
|
||||
max-width: calc(mq.$screen-xl - var(--secondary-nav-width) - var(--primary-nav-width) - var(--layout-padding)*2)
|
||||
}
|
||||
|
||||
pre {
|
||||
|
||||
@@ -109,7 +109,10 @@ describe('DocViewer', () => {
|
||||
expect(exampleViewer).not.toBeNull();
|
||||
expect(exampleViewer.componentInstance.view()).toBe(CodeExampleViewMode.SNIPPET);
|
||||
|
||||
const checkIcon = fixture.debugElement.query(By.directive(IconComponent));
|
||||
const copySourceCodeButton = fixture.debugElement.query(By.directive(CopySourceCodeButton));
|
||||
expect(copySourceCodeButton).not.toBeNull();
|
||||
|
||||
const checkIcon = copySourceCodeButton.query(By.directive(IconComponent));
|
||||
expect((checkIcon.nativeElement as HTMLElement).classList).toContain(
|
||||
`material-symbols-outlined`,
|
||||
);
|
||||
|
||||
@@ -28,21 +28,22 @@ import {
|
||||
effect,
|
||||
} from '@angular/core';
|
||||
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||
import {TOC_SKIP_CONTENT_MARKER, NavigationState} from '../../../services/index';
|
||||
import {TOC_SKIP_CONTENT_MARKER, NavigationState} from '../../../services';
|
||||
import {TableOfContents} from '../../table-of-contents/table-of-contents.component';
|
||||
import {IconComponent} from '../../icon/icon.component';
|
||||
import {handleHrefClickEventWithRouter} from '../../../utils/index';
|
||||
import {Snippet} from '../../../interfaces/index';
|
||||
import {handleHrefClickEventWithRouter} from '../../../utils';
|
||||
import {Snippet} from '../../../interfaces';
|
||||
import {Router} from '@angular/router';
|
||||
import {fromEvent} from 'rxjs';
|
||||
|
||||
import {Breadcrumb} from '../../breadcrumb/breadcrumb.component';
|
||||
import {CopySourceCodeButton} from '../../copy-source-code-button/copy-source-code-button.component';
|
||||
import {ExampleViewer} from '../example-viewer/example-viewer.component';
|
||||
import {DomSanitizer} from '@angular/platform-browser';
|
||||
|
||||
const TOC_HOST_ELEMENT_NAME = 'docs-table-of-contents';
|
||||
export const ASSETS_EXAMPLES_PATH = 'assets/content/examples';
|
||||
export const DOCS_VIEWER_SELECTOR = 'docs-viewer';
|
||||
export const DOCS_VIEWER_SELECTOR = 'docs-viewer, main[docsViewer]';
|
||||
export const DOCS_CODE_SELECTOR = '.docs-code';
|
||||
export const DOCS_CODE_MUTLIFILE_SELECTOR = '.docs-code-multifile';
|
||||
// TODO: Update the branch/sha
|
||||
@@ -60,8 +61,8 @@ export const GITHUB_CONTENT_URL = 'https://github.com/angular/angular/blob/main/
|
||||
},
|
||||
})
|
||||
export class DocViewer {
|
||||
docContent = input<string | undefined>();
|
||||
hasToc = input(false);
|
||||
readonly docContent = input<string | undefined>();
|
||||
readonly hasToc = input(false);
|
||||
readonly contentLoaded = output<void>();
|
||||
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
@@ -74,6 +75,7 @@ export class DocViewer {
|
||||
private readonly environmentInjector = inject(EnvironmentInjector);
|
||||
private readonly injector = inject(Injector);
|
||||
private readonly appRef = inject(ApplicationRef);
|
||||
private readonly sanitizer = inject(DomSanitizer);
|
||||
|
||||
protected animateContent = false;
|
||||
private readonly pendingTasks = inject(PendingTasks);
|
||||
@@ -186,6 +188,7 @@ export class DocViewer {
|
||||
path: string,
|
||||
): Promise<void> {
|
||||
const preview = Boolean(placeholder.getAttribute('preview'));
|
||||
const hideCode = Boolean(placeholder.getAttribute('hideCode'));
|
||||
const title = placeholder.getAttribute('header') ?? undefined;
|
||||
const firstCodeSnippetTitle =
|
||||
snippets.length > 0 ? (snippets[0].title ?? snippets[0].name) : undefined;
|
||||
@@ -197,13 +200,14 @@ export class DocViewer {
|
||||
path,
|
||||
files: snippets,
|
||||
preview,
|
||||
hideCode,
|
||||
id: this.countOfExamples,
|
||||
});
|
||||
|
||||
exampleRef.instance.githubUrl = `${GITHUB_CONTENT_URL}/${snippets[0].name}`;
|
||||
exampleRef.setInput('githubUrl', `${GITHUB_CONTENT_URL}/${snippets[0].name}`);
|
||||
|
||||
// TODO: Re-add support for opening examples on StackBlitz
|
||||
exampleRef.instance.stackblitzUrl = null; // `${ASSETS_EXAMPLES_PATH}/${snippets[0].name}.html`;
|
||||
exampleRef.setInput('stackblitzUrl', null); // `${ASSETS_EXAMPLES_PATH}/${snippets[0].name}.html`;
|
||||
|
||||
placeholder.parentElement!.replaceChild(exampleRef.location.nativeElement, placeholder);
|
||||
|
||||
@@ -215,7 +219,7 @@ export class DocViewer {
|
||||
|
||||
return tabs.map((tab) => ({
|
||||
name: tab.getAttribute('path') ?? tab.getAttribute('header') ?? '',
|
||||
content: tab.innerHTML,
|
||||
sanitizedContent: this.sanitizer.bypassSecurityTrustHtml(tab.innerHTML),
|
||||
visibleLinesRange: tab.getAttribute('visibleLines') ?? undefined,
|
||||
}));
|
||||
}
|
||||
@@ -235,7 +239,9 @@ export class DocViewer {
|
||||
return {
|
||||
title,
|
||||
name: path,
|
||||
content: content?.outerHTML,
|
||||
sanitizedContent: content?.outerHTML
|
||||
? this.sanitizer.bypassSecurityTrustHtml(content.outerHTML)
|
||||
: '',
|
||||
visibleLinesRange: visibleLines,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,27 +1,42 @@
|
||||
<div class="docs-example-viewer" role="group">
|
||||
<header class="docs-example-viewer-actions">
|
||||
@if (view() === CodeExampleViewMode.SNIPPET) {
|
||||
<span>{{ exampleMetadata()?.title }}</span>
|
||||
}
|
||||
@if (showCode()) {
|
||||
@if (view() === CodeExampleViewMode.SNIPPET) {
|
||||
<span>{{ exampleMetadata()?.title }}</span>
|
||||
}
|
||||
|
||||
@if (view() === CodeExampleViewMode.MULTI_FILE) {
|
||||
<mat-tab-group
|
||||
#codeTabs
|
||||
animationDuration="0ms"
|
||||
mat-stretch-tabs="false"
|
||||
>
|
||||
@for (tab of tabs(); track tab) {
|
||||
<mat-tab [label]="tab.name"></mat-tab>
|
||||
}
|
||||
</mat-tab-group>
|
||||
@if (view() === CodeExampleViewMode.MULTI_FILE) {
|
||||
<mat-tab-group #codeTabs animationDuration="0ms" mat-stretch-tabs="false">
|
||||
@for (tab of tabs(); track tab) {
|
||||
<mat-tab [label]="tab.name" />
|
||||
}
|
||||
</mat-tab-group>
|
||||
}
|
||||
} @else {
|
||||
<!-- Title placeholder -->
|
||||
<span aria-hidden="true"> </span>
|
||||
}
|
||||
|
||||
<div class="docs-example-viewer-icons">
|
||||
<button
|
||||
type="button"
|
||||
role="switch"
|
||||
class="docs-example-code-toggle"
|
||||
[attr.aria-checked]="showCode()"
|
||||
[attr.aria-label]="showCode() ? 'Hide code' : 'Show code'"
|
||||
(click)="showCode.set(!showCode())"
|
||||
[matTooltip]="showCode() ? 'Hide code' : 'Show code'"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<docs-icon>{{showCode() ? 'code_off' : 'code'}}</docs-icon>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="docs-example-copy-link"
|
||||
[attr.aria-label]="'Copy link to ' + exampleMetadata()?.title + ' example to the clipboard'"
|
||||
(click)="copyLink()"
|
||||
matTooltip="Copy link to example"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<i aria-hidden="true">
|
||||
<svg
|
||||
@@ -45,8 +60,9 @@
|
||||
<button
|
||||
type="button"
|
||||
(click)="toggleExampleVisibility()"
|
||||
[attr.title]="(expanded() ? 'Collapse' : 'Expand') + ' example'"
|
||||
[attr.aria-label]="(expanded() ? 'Collapse' : 'Expand') + ' code example'"
|
||||
matTooltip="{{ expanded() ? 'Collapse' : 'Expand' }} example"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<i aria-hidden="true">
|
||||
@if (!expanded()) {
|
||||
@@ -81,14 +97,18 @@
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div
|
||||
class="docs-example-viewer-code-wrapper"
|
||||
[class.docs-example-viewer-snippet]="view() === CodeExampleViewMode.SNIPPET"
|
||||
[class.docs-example-viewer-multi-file]="view() === CodeExampleViewMode.MULTI_FILE"
|
||||
>
|
||||
<button docs-copy-source-code></button>
|
||||
<docs-viewer [docContent]="snippetCode()?.content" />
|
||||
</div>
|
||||
@if (showCode()) {
|
||||
<div
|
||||
class="docs-example-viewer-code-wrapper"
|
||||
[class.docs-example-viewer-snippet]="view() === CodeExampleViewMode.SNIPPET"
|
||||
[class.docs-example-viewer-multi-file]="view() === CodeExampleViewMode.MULTI_FILE"
|
||||
>
|
||||
<button docs-copy-source-code></button>
|
||||
@if (snippetCode()?.sanitizedContent; as content) {
|
||||
<div [innerHTML]="content"></div>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (exampleComponent) {
|
||||
<div class="docs-example-viewer-preview">
|
||||
@@ -98,13 +118,14 @@
|
||||
|
||||
<ng-template #openCodeInExternalProvider>
|
||||
@if (exampleComponent) {
|
||||
@if (githubUrl) {
|
||||
@if (githubUrl()) {
|
||||
<a
|
||||
[href]="githubUrl"
|
||||
[href]="githubUrl()"
|
||||
target="_blank"
|
||||
title="Open example on GitHub"
|
||||
class="docs-example-github-link"
|
||||
aria-label="Open example on GitHub"
|
||||
matTooltip="Open example on GitHub"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<i aria-hidden="true">
|
||||
<svg
|
||||
@@ -125,13 +146,14 @@
|
||||
</i>
|
||||
</a>
|
||||
}
|
||||
@if (stackblitzUrl) {
|
||||
@if (stackblitzUrl()) {
|
||||
<a
|
||||
[href]="stackblitzUrl"
|
||||
[href]="stackblitzUrl()"
|
||||
target="_blank"
|
||||
class="docs-example-stackblitz-link"
|
||||
title="Edit this example in StackBlitz"
|
||||
aria-label="Edit this example in StackBlitz"
|
||||
aria-label="Edit example in StackBlitz"
|
||||
matTooltip="Edit example in StackBlitz"
|
||||
matTooltipPosition="above"
|
||||
>
|
||||
<i aria-hidden="true">
|
||||
<svg
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
:host {
|
||||
.docs-example-viewer-preview {
|
||||
.docs-dark-mode & {
|
||||
background: var(--gray-100);
|
||||
color: var(--primary-contrast);
|
||||
background: var(--gray-900);
|
||||
}
|
||||
@media screen and (prefers-color-scheme: dark) {
|
||||
background: var(--gray-100);
|
||||
color: var(--primary-contrast);
|
||||
background: var(--gray-900);
|
||||
}
|
||||
.docs-light-mode & {
|
||||
background: var(--page-background);
|
||||
@@ -55,25 +57,25 @@
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
|
||||
svg {
|
||||
fill: var(--gray-400);
|
||||
}
|
||||
}
|
||||
a,
|
||||
button {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
color: var(--gray-400);
|
||||
|
||||
a,
|
||||
button {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
path {
|
||||
transition: fill 0.3s ease;
|
||||
}
|
||||
path {
|
||||
transition: fill 0.3s ease;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
svg {
|
||||
fill: var(--tertiary-contrast);
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--tertiary-contrast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+65
-20
@@ -35,7 +35,6 @@ describe('ExampleViewer', () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ExampleViewer],
|
||||
providers: [
|
||||
// TODO: Find why tests warn that zone.js is still loaded
|
||||
provideZonelessChangeDetection(),
|
||||
{provide: EXAMPLE_VIEWER_CONTENT_LOADER, useValue: exampleContentSpy},
|
||||
{provide: ActivatedRoute, useValue: {snapshot: {fragment: 'fragment'}}},
|
||||
@@ -53,9 +52,9 @@ describe('ExampleViewer', () => {
|
||||
'metadata',
|
||||
getMetadata({
|
||||
files: [
|
||||
{name: 'file.ts', content: ''},
|
||||
{name: 'file.html', content: ''},
|
||||
{name: 'file.css', content: ''},
|
||||
{name: 'file.ts', sanitizedContent: ''},
|
||||
{name: 'file.html', sanitizedContent: ''},
|
||||
{name: 'file.css', sanitizedContent: ''},
|
||||
],
|
||||
}),
|
||||
);
|
||||
@@ -73,9 +72,9 @@ describe('ExampleViewer', () => {
|
||||
'metadata',
|
||||
getMetadata({
|
||||
files: [
|
||||
{name: 'file.ts', content: 'typescript file'},
|
||||
{name: 'file.html', content: 'html file'},
|
||||
{name: 'file.css', content: 'css file'},
|
||||
{name: 'file.ts', sanitizedContent: 'typescript file'},
|
||||
{name: 'file.html', sanitizedContent: 'html file'},
|
||||
{name: 'file.css', sanitizedContent: 'css file'},
|
||||
],
|
||||
}),
|
||||
);
|
||||
@@ -93,9 +92,9 @@ describe('ExampleViewer', () => {
|
||||
'metadata',
|
||||
getMetadata({
|
||||
files: [
|
||||
{name: 'example.ts', content: 'typescript file'},
|
||||
{name: 'example.html', content: 'html file'},
|
||||
{name: 'another-example.ts', content: 'css file'},
|
||||
{name: 'example.ts', sanitizedContent: 'typescript file'},
|
||||
{name: 'example.html', sanitizedContent: 'html file'},
|
||||
{name: 'another-example.ts', sanitizedContent: 'css file'},
|
||||
],
|
||||
}),
|
||||
);
|
||||
@@ -124,7 +123,7 @@ describe('ExampleViewer', () => {
|
||||
files: [
|
||||
{
|
||||
name: 'example.ts',
|
||||
content: `<pre><code>${expectedCodeSnippetContent}</code></pre>`,
|
||||
sanitizedContent: `<pre><code>${expectedCodeSnippetContent}</code></pre>`,
|
||||
visibleLinesRange: '[1]',
|
||||
},
|
||||
],
|
||||
@@ -148,7 +147,7 @@ describe('ExampleViewer', () => {
|
||||
files: [
|
||||
{
|
||||
name: 'example.ts',
|
||||
content: `<pre><code>${expectedCodeSnippetContent}</code></pre>`,
|
||||
sanitizedContent: `<pre><code>${expectedCodeSnippetContent}</code></pre>`,
|
||||
visibleLinesRange: '[1]',
|
||||
},
|
||||
],
|
||||
@@ -198,7 +197,7 @@ describe('ExampleViewer', () => {
|
||||
By.css('a[aria-label="Open example on GitHub"]'),
|
||||
);
|
||||
expect(githubButton).toBeTruthy();
|
||||
expect(githubButton.nativeElement.href).toBe(component.githubUrl);
|
||||
expect(githubButton.nativeElement.href).toBe(component.githubUrl());
|
||||
});
|
||||
|
||||
it('should display StackBlitz button when stackblitzUrl is provided and there is preview', async () => {
|
||||
@@ -210,15 +209,16 @@ describe('ExampleViewer', () => {
|
||||
preview: true,
|
||||
}),
|
||||
);
|
||||
component.stackblitzUrl = 'https://stackblitz.com/';
|
||||
componentRef.setInput('stackblitzUrl', 'https://stackblitz.com/');
|
||||
|
||||
await component.renderExample();
|
||||
fixture.detectChanges();
|
||||
|
||||
const stackblitzButton = fixture.debugElement.query(
|
||||
By.css('a[aria-label="Edit this example in StackBlitz"]'),
|
||||
By.css('a[aria-label="Edit example in StackBlitz"]'),
|
||||
);
|
||||
expect(stackblitzButton).toBeTruthy();
|
||||
expect(stackblitzButton.nativeElement.href).toBe(component.stackblitzUrl);
|
||||
expect(stackblitzButton.nativeElement.href).toBe(component.stackblitzUrl());
|
||||
});
|
||||
|
||||
it('should set expanded flag in metadata after toggleExampleVisibility', async () => {
|
||||
@@ -242,9 +242,9 @@ describe('ExampleViewer', () => {
|
||||
files: [
|
||||
{
|
||||
name: 'example.ts',
|
||||
content: `<pre><code>${expectedCodeSnippetContent}</code></pre>`,
|
||||
sanitizedContent: `<pre><code>${expectedCodeSnippetContent}</code></pre>`,
|
||||
},
|
||||
{name: 'example.css', content: ''},
|
||||
{name: 'example.css', sanitizedContent: ''},
|
||||
],
|
||||
}),
|
||||
);
|
||||
@@ -272,16 +272,61 @@ describe('ExampleViewer', () => {
|
||||
button.click();
|
||||
expect(spy.calls.argsFor(0)[0].trim()).toBe(`${window.location.href}#example-1`);
|
||||
});
|
||||
|
||||
it('should hide code content when `hideCode` is true', async () => {
|
||||
componentRef.setInput(
|
||||
'metadata',
|
||||
getMetadata({
|
||||
hideCode: true,
|
||||
}),
|
||||
);
|
||||
|
||||
await component.renderExample();
|
||||
fixture.detectChanges();
|
||||
|
||||
// Initially, the code should be hidden.
|
||||
expect(component.showCode()).toBeFalse();
|
||||
let codeContainer = fixture.debugElement.query(By.css('.docs-example-viewer-code-wrapper'));
|
||||
expect(codeContainer).toBeNull();
|
||||
});
|
||||
|
||||
it('should expand/collapse code content with toggle button.', async () => {
|
||||
componentRef.setInput('metadata', getMetadata());
|
||||
|
||||
await component.renderExample();
|
||||
fixture.detectChanges();
|
||||
|
||||
// Initially, the code should be visible.
|
||||
expect(component.showCode()).toBeTrue();
|
||||
let codeContainer = fixture.debugElement.query(By.css('.docs-example-viewer-code-wrapper'));
|
||||
expect(codeContainer).not.toBeNull();
|
||||
|
||||
const codeToggleButton = fixture.debugElement.query(By.css('.docs-example-code-toggle'));
|
||||
codeToggleButton.nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.showCode()).toBeFalse();
|
||||
codeContainer = fixture.debugElement.query(By.css('.docs-example-viewer-code-wrapper'));
|
||||
expect(codeContainer).toBeNull();
|
||||
|
||||
codeToggleButton.nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.showCode()).toBeTrue();
|
||||
codeContainer = fixture.debugElement.query(By.css('.docs-example-viewer-code-wrapper'));
|
||||
expect(codeContainer).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
const getMetadata = (value: Partial<ExampleMetadata> = {}): ExampleMetadata => {
|
||||
return {
|
||||
id: 1,
|
||||
files: [
|
||||
{name: 'example.ts', content: ''},
|
||||
{name: 'example.css', content: ''},
|
||||
{name: 'example.ts', sanitizedContent: ''},
|
||||
{name: 'example.css', sanitizedContent: ''},
|
||||
],
|
||||
preview: false,
|
||||
hideCode: false,
|
||||
...value,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -14,23 +14,22 @@ import {
|
||||
computed,
|
||||
DestroyRef,
|
||||
ElementRef,
|
||||
forwardRef,
|
||||
inject,
|
||||
Injector,
|
||||
input,
|
||||
Input,
|
||||
signal,
|
||||
Type,
|
||||
viewChild,
|
||||
} from '@angular/core';
|
||||
import {CommonModule, DOCUMENT} from '@angular/common';
|
||||
import {DOCUMENT, NgTemplateOutlet} from '@angular/common';
|
||||
import {MatTabGroup, MatTabsModule} from '@angular/material/tabs';
|
||||
import {Clipboard} from '@angular/cdk/clipboard';
|
||||
import {CopySourceCodeButton} from '../../copy-source-code-button/copy-source-code-button.component';
|
||||
import {IconComponent} from '../../icon/icon.component';
|
||||
import {ExampleMetadata, Snippet} from '../../../interfaces/index';
|
||||
import {EXAMPLE_VIEWER_CONTENT_LOADER} from '../../../providers/index';
|
||||
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||
import {DocViewer} from '../docs-viewer/docs-viewer.component';
|
||||
import {MatTooltipModule} from '@angular/material/tooltip';
|
||||
|
||||
export enum CodeExampleViewMode {
|
||||
SNIPPET = 'snippet',
|
||||
@@ -44,16 +43,15 @@ export const HIDDEN_CLASS_NAME = 'hidden';
|
||||
|
||||
@Component({
|
||||
selector: 'docs-example-viewer',
|
||||
imports: [CommonModule, forwardRef(() => DocViewer), CopySourceCodeButton, MatTabsModule],
|
||||
imports: [CopySourceCodeButton, MatTabsModule, MatTooltipModule, IconComponent, NgTemplateOutlet],
|
||||
templateUrl: './example-viewer.component.html',
|
||||
styleUrls: ['./example-viewer.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ExampleViewer {
|
||||
exampleMetadata = input<ExampleMetadata | null>(null, {alias: 'metadata'});
|
||||
|
||||
@Input() githubUrl: string | null = null;
|
||||
@Input() stackblitzUrl: string | null = null;
|
||||
readonly exampleMetadata = input<ExampleMetadata | null>(null, {alias: 'metadata'});
|
||||
readonly githubUrl = input<string | null>(null);
|
||||
readonly stackblitzUrl = input<string | null>(null);
|
||||
readonly matTabGroup = viewChild<MatTabGroup>('codeTabs');
|
||||
|
||||
private readonly changeDetector = inject(ChangeDetectorRef);
|
||||
@@ -75,17 +73,18 @@ export class ExampleViewer {
|
||||
CodeExampleViewMode = CodeExampleViewMode;
|
||||
exampleComponent?: Type<unknown>;
|
||||
|
||||
expandable = signal<boolean>(false);
|
||||
expanded = signal<boolean>(false);
|
||||
snippetCode = signal<Snippet | undefined>(undefined);
|
||||
tabs = computed(() =>
|
||||
readonly expandable = signal<boolean>(false);
|
||||
readonly expanded = signal<boolean>(false);
|
||||
readonly snippetCode = signal<Snippet | undefined>(undefined);
|
||||
readonly showCode = signal<boolean>(true);
|
||||
readonly tabs = computed(() =>
|
||||
this.exampleMetadata()?.files.map((file) => ({
|
||||
name:
|
||||
file.title ?? (this.shouldDisplayFullName() ? file.name : this.getFileExtension(file.name)),
|
||||
code: file.content,
|
||||
code: file.sanitizedContent,
|
||||
})),
|
||||
);
|
||||
view = computed(() =>
|
||||
readonly view = computed(() =>
|
||||
this.exampleMetadata()?.files.length === 1
|
||||
? CodeExampleViewMode.SNIPPET
|
||||
: CodeExampleViewMode.MULTI_FILE,
|
||||
@@ -101,6 +100,10 @@ export class ExampleViewer {
|
||||
|
||||
this.snippetCode.set(this.exampleMetadata()?.files[0]);
|
||||
|
||||
if (this.exampleMetadata()?.hideCode) {
|
||||
this.showCode.set(false);
|
||||
}
|
||||
|
||||
afterNextRender(
|
||||
() => {
|
||||
// Several function below query the DOM directly, we need to wait until the DOM is rendered.
|
||||
|
||||
@@ -3,7 +3,7 @@ load("@aspect_bazel_lib//lib:copy_to_directory.bzl", _copy_to_directory = "copy_
|
||||
load("@aspect_rules_esbuild//esbuild:defs.bzl", _esbuild = "esbuild")
|
||||
load("@devinfra//bazel/private:path_relative_to_label.bzl", _path_relative_to_label = "path_relative_to_label")
|
||||
load(
|
||||
"//tools:defaults2.bzl",
|
||||
"//tools:defaults.bzl",
|
||||
_jasmine_test = "jasmine_test",
|
||||
_js_binary = "js_binary",
|
||||
_js_library = "js_library",
|
||||
|
||||
@@ -8,3 +8,4 @@
|
||||
|
||||
export * from './click-outside/click-outside.directive';
|
||||
export * from './external-link/external-link.directive';
|
||||
export * from './search-item/search-item.directive';
|
||||
|
||||
@@ -21,7 +21,7 @@ export class SearchItem implements Highlightable {
|
||||
// We can't migrate them to signals yet
|
||||
@Input() disabled = false;
|
||||
|
||||
item = input<SearchResultItem | undefined>();
|
||||
readonly item = input<SearchResultItem | undefined>();
|
||||
|
||||
private readonly elementRef = inject(ElementRef<HTMLLIElement>);
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ ts_project(
|
||||
),
|
||||
deps = [
|
||||
"//adev:node_modules/@angular/core",
|
||||
"//adev:node_modules/@angular/platform-browser",
|
||||
"//adev:node_modules/@angular/router",
|
||||
"//adev:node_modules/@types/node",
|
||||
"//adev:node_modules/@webcontainer/api",
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import {Type} from '@angular/core';
|
||||
import {SafeHtml} from '@angular/platform-browser';
|
||||
|
||||
/**
|
||||
* Map of the examples, values are functions which returns the promise of the component type, which will be displayed as preview in the ExampleViewer component
|
||||
@@ -21,7 +22,7 @@ export interface Snippet {
|
||||
/** Name of the file. */
|
||||
name: string;
|
||||
/** Content of code snippet */
|
||||
content: string;
|
||||
sanitizedContent: SafeHtml;
|
||||
/** Text in following format `start-end`. Start and end are numbers, based on them provided range of lines will be displayed in collapsed mode */
|
||||
visibleLinesRange?: string;
|
||||
}
|
||||
@@ -37,4 +38,6 @@ export interface ExampleMetadata {
|
||||
files: Snippet[];
|
||||
/** True when ExampleViewer should have preview */
|
||||
preview: boolean;
|
||||
/** Whether to hide code example by default. */
|
||||
hideCode: boolean;
|
||||
}
|
||||
|
||||
@@ -16,4 +16,5 @@ export interface NavigationItem {
|
||||
parent?: NavigationItem;
|
||||
contentPath?: string;
|
||||
status?: 'new' | 'updated';
|
||||
category?: string;
|
||||
}
|
||||
|
||||
@@ -60,6 +60,8 @@ export interface SearchResultItem {
|
||||
type: 'doc' | 'code';
|
||||
labelHtml: string | null;
|
||||
subLabelHtml: string | null;
|
||||
contentHtml: string | null;
|
||||
package: string | null;
|
||||
url: string;
|
||||
|
||||
id: string;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"fflate": "^0.8.2",
|
||||
"html-entities": "~2.6.0",
|
||||
"jsdom": "~26.1.0",
|
||||
"marked": "~16.1.0",
|
||||
"marked": "~16.2.0",
|
||||
"mermaid": "^11.0.0",
|
||||
"shiki": "^3.0.0",
|
||||
"tinyglobby": "^0.2.12"
|
||||
|
||||
@@ -47,13 +47,13 @@ js_binary(
|
||||
"//adev:node_modules/jsdom",
|
||||
"//adev:node_modules/mermaid",
|
||||
"//adev:node_modules/playwright-core",
|
||||
"@rules_browsers//src/browsers/chromium",
|
||||
"@rules_browsers//browsers/chromium",
|
||||
],
|
||||
entry_point = "//adev/shared-docs/pipeline/guides:guides.mjs",
|
||||
env = {
|
||||
"CHROME_HEADLESS_BIN": "$(CHROME-HEADLESS-SHELL)",
|
||||
},
|
||||
toolchains = ["@rules_browsers//src/browsers/chromium:toolchain_alias"],
|
||||
toolchains = ["@rules_browsers//browsers/chromium:toolchain_alias"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
|
||||
@@ -16,6 +16,12 @@ def _generate_guides(ctx):
|
||||
# Pass the the output directory path (which is the bazel-bin directory).
|
||||
args.add(ctx.bin_dir.path)
|
||||
|
||||
# Add the api_manifest file if provided
|
||||
if ctx.attr.api_manifest:
|
||||
args.add(ctx.file.api_manifest.path)
|
||||
else:
|
||||
args.add("")
|
||||
|
||||
# Determine the set of html output files. For each input markdown file, produce an html
|
||||
# file with the same name (replacing the markdown extension with `.html`).
|
||||
html_outputs = []
|
||||
@@ -30,9 +36,13 @@ def _generate_guides(ctx):
|
||||
html_outputs += [ctx.actions.declare_file("%s.html" % relative_basepath)]
|
||||
|
||||
# Define an action that runs the executable.
|
||||
inputs = ctx.files.srcs + ctx.files.data
|
||||
if ctx.attr.api_manifest:
|
||||
inputs.append(ctx.file.api_manifest)
|
||||
|
||||
if (ctx.attr.mermaid_blocks):
|
||||
ctx.actions.run(
|
||||
inputs = depset(ctx.files.srcs + ctx.files.data),
|
||||
inputs = depset(inputs),
|
||||
executable = ctx.executable._generate_guides,
|
||||
outputs = html_outputs,
|
||||
arguments = [args],
|
||||
@@ -42,7 +52,7 @@ def _generate_guides(ctx):
|
||||
)
|
||||
else:
|
||||
ctx.actions.run(
|
||||
inputs = depset(ctx.files.srcs + ctx.files.data),
|
||||
inputs = depset(inputs),
|
||||
executable = ctx.executable._generate_guides_no_mermaid,
|
||||
outputs = html_outputs,
|
||||
arguments = [args],
|
||||
@@ -71,6 +81,10 @@ generate_guides = rule(
|
||||
doc = """Source referenced from within the markdown.""",
|
||||
allow_files = True,
|
||||
),
|
||||
"api_manifest": attr.label(
|
||||
doc = """A file containing API entries to be used in the markdown.""",
|
||||
allow_single_file = True,
|
||||
),
|
||||
"mermaid_blocks": attr.bool(
|
||||
doc = """Whether to transform mermaid blocks.""",
|
||||
default = False,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
load("@npm2//:defs.bzl", "npm_link_all_packages")
|
||||
load("@npm//:defs.bzl", "npm_link_all_packages")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ function main() {
|
||||
...getEntriesFromMembers(entry).map((member) => [member, moduleName]),
|
||||
]),
|
||||
],
|
||||
} as EntryCollection);
|
||||
} satisfies EntryCollection);
|
||||
|
||||
writeFileSync(outputFilenameExecRootRelativePath, output, {encoding: 'utf8'});
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import type {DocEntry, EntryCollection, FunctionEntry, JsDocTagEntry} from '@ang
|
||||
export interface ManifestEntry {
|
||||
name: string;
|
||||
type: string;
|
||||
category: string | undefined;
|
||||
deprecated: {version: string | undefined} | undefined;
|
||||
developerPreview: {version: string | undefined} | undefined;
|
||||
experimental: {version: string | undefined} | undefined;
|
||||
@@ -85,6 +86,7 @@ export function generateManifest(apiCollections: EntryCollection[]): Manifest {
|
||||
developerPreview: getTagSinceVersion(entry, 'developerPreview'),
|
||||
experimental: getTagSinceVersion(entry, 'experimental'),
|
||||
stable: getTagSinceVersion(entry, 'publicApi'),
|
||||
category: getTag(entry, 'category')?.comment.trim(),
|
||||
}));
|
||||
|
||||
const existingEntry = manifest.find((entry) => entry.moduleName === collection.moduleName);
|
||||
|
||||
@@ -14,18 +14,21 @@ describe('api manifest generation', () => {
|
||||
it('should generate a manifest from multiple collections', () => {
|
||||
const manifest: Manifest = generateManifest([
|
||||
{
|
||||
repo: 'angular/router',
|
||||
moduleName: '@angular/router',
|
||||
entries: [entry({name: 'Router', entryType: EntryType.UndecoratedClass})],
|
||||
normalizedModuleName: 'angular_router',
|
||||
moduleLabel: 'router',
|
||||
},
|
||||
{
|
||||
repo: 'angular/core',
|
||||
moduleName: '@angular/core',
|
||||
entries: [entry({name: 'PI', entryType: EntryType.Constant})],
|
||||
normalizedModuleName: 'angular_core',
|
||||
moduleLabel: 'core',
|
||||
},
|
||||
{
|
||||
repo: 'angular/core',
|
||||
moduleName: '@angular/core',
|
||||
entries: [entry({name: 'foo', entryType: EntryType.Constant})],
|
||||
normalizedModuleName: 'angular_core',
|
||||
@@ -47,6 +50,7 @@ describe('api manifest generation', () => {
|
||||
deprecated: undefined,
|
||||
experimental: undefined,
|
||||
stable: undefined,
|
||||
category: undefined,
|
||||
},
|
||||
{
|
||||
name: 'PI',
|
||||
@@ -55,6 +59,7 @@ describe('api manifest generation', () => {
|
||||
deprecated: undefined,
|
||||
experimental: undefined,
|
||||
stable: undefined,
|
||||
category: undefined,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -70,6 +75,7 @@ describe('api manifest generation', () => {
|
||||
deprecated: undefined,
|
||||
experimental: undefined,
|
||||
stable: undefined,
|
||||
category: undefined,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -79,12 +85,14 @@ describe('api manifest generation', () => {
|
||||
it('should generate a manifest when collections share a symbol with the same name', () => {
|
||||
const manifest = generateManifest([
|
||||
{
|
||||
repo: 'angular/core',
|
||||
moduleName: '@angular/core',
|
||||
entries: [entry({name: 'PI', entryType: EntryType.Constant})],
|
||||
normalizedModuleName: 'angular_core',
|
||||
moduleLabel: 'core',
|
||||
},
|
||||
{
|
||||
repo: 'angular/router',
|
||||
moduleName: '@angular/router',
|
||||
entries: [entry({name: 'PI', entryType: EntryType.Constant})],
|
||||
normalizedModuleName: 'angular_router',
|
||||
@@ -105,6 +113,7 @@ describe('api manifest generation', () => {
|
||||
deprecated: undefined,
|
||||
experimental: undefined,
|
||||
stable: undefined,
|
||||
category: undefined,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -120,6 +129,7 @@ describe('api manifest generation', () => {
|
||||
deprecated: undefined,
|
||||
experimental: undefined,
|
||||
stable: undefined,
|
||||
category: undefined,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -129,12 +139,14 @@ describe('api manifest generation', () => {
|
||||
it('should union collections for the same module into one manifest', () => {
|
||||
const manifest = generateManifest([
|
||||
{
|
||||
repo: 'angular/core',
|
||||
moduleName: '@angular/core',
|
||||
entries: [entry({name: 'PI', entryType: EntryType.Constant})],
|
||||
normalizedModuleName: 'angular_core',
|
||||
moduleLabel: 'core',
|
||||
},
|
||||
{
|
||||
repo: 'angular/core',
|
||||
moduleName: '@angular/core',
|
||||
entries: [entry({name: 'TAO', entryType: EntryType.Constant})],
|
||||
normalizedModuleName: 'angular_core',
|
||||
@@ -155,6 +167,7 @@ describe('api manifest generation', () => {
|
||||
deprecated: undefined,
|
||||
experimental: undefined,
|
||||
stable: undefined,
|
||||
category: undefined,
|
||||
},
|
||||
{
|
||||
name: 'TAO',
|
||||
@@ -163,6 +176,7 @@ describe('api manifest generation', () => {
|
||||
deprecated: undefined,
|
||||
experimental: undefined,
|
||||
stable: undefined,
|
||||
category: undefined,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -172,6 +186,7 @@ describe('api manifest generation', () => {
|
||||
it('should mark a manifest entry as deprecated', () => {
|
||||
const manifest = generateManifest([
|
||||
{
|
||||
repo: 'angular/core',
|
||||
moduleName: '@angular/core',
|
||||
entries: [
|
||||
entry({name: 'PI', entryType: EntryType.Constant, jsdocTags: jsdocTags('deprecated')}),
|
||||
@@ -195,6 +210,7 @@ describe('api manifest generation', () => {
|
||||
deprecated: {version: undefined},
|
||||
experimental: undefined,
|
||||
stable: undefined,
|
||||
category: undefined,
|
||||
},
|
||||
{
|
||||
name: 'XI',
|
||||
@@ -203,6 +219,7 @@ describe('api manifest generation', () => {
|
||||
deprecated: undefined,
|
||||
experimental: {version: undefined},
|
||||
stable: undefined,
|
||||
category: undefined,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -212,6 +229,7 @@ describe('api manifest generation', () => {
|
||||
it('should not mark a function as deprecated if only one overload is deprecated', () => {
|
||||
const manifest = generateManifest([
|
||||
{
|
||||
repo: 'angular/core',
|
||||
moduleName: '@angular/core',
|
||||
entries: [
|
||||
functionEntry({
|
||||
@@ -262,6 +280,7 @@ describe('api manifest generation', () => {
|
||||
deprecated: undefined,
|
||||
experimental: undefined,
|
||||
stable: undefined,
|
||||
category: undefined,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -271,6 +290,7 @@ describe('api manifest generation', () => {
|
||||
it('should mark a function as deprecated if all overloads are deprecated', () => {
|
||||
const manifest = generateManifest([
|
||||
{
|
||||
repo: 'angular/core',
|
||||
moduleName: '@angular/core',
|
||||
entries: [
|
||||
functionEntry({
|
||||
@@ -321,6 +341,7 @@ describe('api manifest generation', () => {
|
||||
deprecated: {version: undefined},
|
||||
experimental: undefined,
|
||||
stable: undefined,
|
||||
category: undefined,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -330,6 +351,7 @@ describe('api manifest generation', () => {
|
||||
it("should mark a fn as deprecated if there's one w/ the same name in another collection", () => {
|
||||
const manifest = generateManifest([
|
||||
{
|
||||
repo: 'angular/core',
|
||||
moduleName: '@angular/core',
|
||||
entries: [
|
||||
entry({name: 'save', entryType: EntryType.Function, jsdocTags: jsdocTags('deprecated')}),
|
||||
@@ -338,6 +360,7 @@ describe('api manifest generation', () => {
|
||||
moduleLabel: 'core',
|
||||
},
|
||||
{
|
||||
repo: 'angular/more',
|
||||
moduleName: '@angular/more',
|
||||
entries: [entry({name: 'save', entryType: EntryType.Function})],
|
||||
normalizedModuleName: 'angular_more',
|
||||
@@ -358,6 +381,7 @@ describe('api manifest generation', () => {
|
||||
deprecated: {version: undefined},
|
||||
experimental: undefined,
|
||||
stable: undefined,
|
||||
category: undefined,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -373,6 +397,7 @@ describe('api manifest generation', () => {
|
||||
deprecated: undefined,
|
||||
experimental: undefined,
|
||||
stable: undefined,
|
||||
category: undefined,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -382,6 +407,7 @@ describe('api manifest generation', () => {
|
||||
it('should mark a manifest entry as developerPreview', () => {
|
||||
const manifest = generateManifest([
|
||||
{
|
||||
repo: 'angular/core',
|
||||
moduleName: '@angular/core',
|
||||
entries: [
|
||||
entry({
|
||||
@@ -409,6 +435,7 @@ describe('api manifest generation', () => {
|
||||
deprecated: undefined,
|
||||
experimental: undefined,
|
||||
stable: undefined,
|
||||
category: undefined,
|
||||
},
|
||||
{
|
||||
name: 'XI',
|
||||
@@ -417,6 +444,7 @@ describe('api manifest generation', () => {
|
||||
deprecated: undefined,
|
||||
experimental: {version: undefined},
|
||||
stable: undefined,
|
||||
category: undefined,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -5,6 +5,10 @@ package(default_visibility = ["//adev/shared-docs/pipeline/api-gen:__subpackages
|
||||
esbuild(
|
||||
name = "bin",
|
||||
entry_point = ":index.mts",
|
||||
external = [
|
||||
"jsdom",
|
||||
"playwright-core",
|
||||
],
|
||||
format = "esm",
|
||||
output = "bin.mjs",
|
||||
platform = "node",
|
||||
@@ -20,7 +24,6 @@ ts_project(
|
||||
[
|
||||
"**/*.mts",
|
||||
"**/*.tsx",
|
||||
"shiki.d.ts",
|
||||
],
|
||||
exclude = [
|
||||
"entities.mts",
|
||||
@@ -28,13 +31,15 @@ ts_project(
|
||||
),
|
||||
deps = [
|
||||
":entities",
|
||||
"//adev:node_modules/@types/node",
|
||||
"//adev:node_modules/html-entities",
|
||||
"//adev:node_modules/marked",
|
||||
"//adev:node_modules/preact",
|
||||
"//adev:node_modules/preact-render-to-string",
|
||||
"//adev:node_modules/prettier",
|
||||
"//adev:node_modules/shiki",
|
||||
"//adev/shared-docs/pipeline/shared:linking",
|
||||
"//adev/shared-docs/pipeline/shared:shiki",
|
||||
"//adev/shared-docs/pipeline/shared/marked",
|
||||
"//adev/shared-docs/pipeline/shared/regions",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -11,12 +11,12 @@ import path from 'path';
|
||||
import {CliCommand} from './cli-entities.mjs';
|
||||
import {DocEntry} from './entities.mjs';
|
||||
import {isCliEntry, isHiddenEntry} from './entities/categorization.mjs';
|
||||
import {configureMarkedGlobally} from './marked/configuration.mjs';
|
||||
import {getRenderable} from './processing.mjs';
|
||||
import {renderEntry} from './rendering.mjs';
|
||||
import {initHighlighter} from './shiki/shiki.mjs';
|
||||
import {setCurrentSymbol, setSymbols} from './symbol-context.mjs';
|
||||
import {CliCommandRenderable, DocEntryRenderable} from './entities/renderables.mjs';
|
||||
import {initHighlighter} from '../../shared/shiki.mjs';
|
||||
import {setHighlighterInstance} from './shiki/shiki.mjs';
|
||||
|
||||
/** The JSON data file format for extracted API reference info. */
|
||||
interface EntryCollection {
|
||||
@@ -25,7 +25,7 @@ interface EntryCollection {
|
||||
moduleLabel?: string;
|
||||
normalizedModuleName: string;
|
||||
entries: DocEntry[] | CliCommand[];
|
||||
symbols: Map<string, string>;
|
||||
symbols: Record<string, string>;
|
||||
}
|
||||
|
||||
/** Parse all JSON data source files into an array of collections. */
|
||||
@@ -34,10 +34,15 @@ function parseEntryData(srcs: string[]): EntryCollection[] {
|
||||
const fileContent = readFileSync(jsonDataFilePath, {encoding: 'utf8'});
|
||||
const fileContentJson = JSON.parse(fileContent) as unknown;
|
||||
if ((fileContentJson as EntryCollection).entries) {
|
||||
return {
|
||||
...(fileContentJson as EntryCollection),
|
||||
symbols: new Map((fileContentJson as any).symbols ?? []),
|
||||
};
|
||||
const symbols = Object.fromEntries(
|
||||
// TODO: refactor that, it's dirty and we can probably do better than this.
|
||||
// We're removing the leading `@angular/` from module names.
|
||||
(((fileContentJson as any).symbols ?? []) as [string, string][]).map(
|
||||
([symbol, moduleName]) => [symbol, moduleName.slice(9)],
|
||||
),
|
||||
);
|
||||
|
||||
return {...(fileContentJson as EntryCollection), symbols};
|
||||
}
|
||||
|
||||
// CLI subcommands should generate a separate file for each subcommand.
|
||||
@@ -50,7 +55,7 @@ function parseEntryData(srcs: string[]): EntryCollection[] {
|
||||
moduleName: 'unknown',
|
||||
normalizedModuleName: 'unknown',
|
||||
entries: [fileContentJson as DocEntry],
|
||||
symbols: new Map(),
|
||||
symbols: {},
|
||||
},
|
||||
...command.subcommands!.map((subCommand) => {
|
||||
return {
|
||||
@@ -58,7 +63,7 @@ function parseEntryData(srcs: string[]): EntryCollection[] {
|
||||
moduleName: 'unknown',
|
||||
normalizedModuleName: 'unknown',
|
||||
entries: [{...subCommand, parentCommand: command} as any],
|
||||
symbols: new Map(),
|
||||
symbols: {},
|
||||
};
|
||||
}),
|
||||
];
|
||||
@@ -69,7 +74,7 @@ function parseEntryData(srcs: string[]): EntryCollection[] {
|
||||
moduleName: 'unknown',
|
||||
normalizedModuleName: 'unknown',
|
||||
entries: [fileContentJson as DocEntry], // TODO: fix the typing cli entries aren't DocEntry
|
||||
symbols: new Map(),
|
||||
symbols: {},
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -90,23 +95,14 @@ function getNormalizedFilename(normalizedModuleName: string, entry: DocEntry | C
|
||||
}
|
||||
|
||||
async function main() {
|
||||
configureMarkedGlobally();
|
||||
|
||||
// Shiki highlighter needs to be setup in an async context
|
||||
await initHighlighter();
|
||||
setHighlighterInstance(await initHighlighter());
|
||||
|
||||
const [paramFilePath] = process.argv.slice(2);
|
||||
const rawParamLines = readFileSync(paramFilePath, {encoding: 'utf8'}).split('\n');
|
||||
|
||||
const [srcs, outputFilenameExecRootRelativePath] = rawParamLines;
|
||||
|
||||
// TODO: Remove when we are using Bazel v6+
|
||||
// On RBE, the output directory is not created properly due to a bug.
|
||||
// https://github.com/bazelbuild/bazel/commit/4310aeb36c134e5fc61ed5cdfdf683f3e95f19b7.
|
||||
if (!existsSync(outputFilenameExecRootRelativePath)) {
|
||||
mkdirSync(outputFilenameExecRootRelativePath, {recursive: true});
|
||||
}
|
||||
|
||||
// Docs rendering happens in three phases that occur here:
|
||||
// 1) Aggregate all the raw extracted doc info.
|
||||
// 2) Transform the raw data to a renderable state.
|
||||
|
||||
@@ -1,17 +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 {marked} from 'marked';
|
||||
import {renderer} from './renderer.mjs';
|
||||
|
||||
/** Globally configures marked for rendering JsDoc content to HTML. */
|
||||
export function configureMarkedGlobally() {
|
||||
marked.use({
|
||||
renderer,
|
||||
});
|
||||
}
|
||||
@@ -1,92 +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 {Renderer, Tokens} from 'marked';
|
||||
import {codeToHtml} from '../shiki/shiki.mjs';
|
||||
import {SECTION_HEADING, SECTION_SUB_HEADING} from '../styling/css-classes.mjs';
|
||||
|
||||
/**
|
||||
* Custom renderer for marked that will be used to transform markdown files to HTML
|
||||
* files that can be used in the Angular docs.
|
||||
*/
|
||||
export const renderer: Partial<Renderer> = {
|
||||
code({lang, text}): string {
|
||||
const highlightResult = codeToHtml(text, lang)
|
||||
// remove spaces/line-breaks between elements to not mess-up `pre` style
|
||||
.replace(/>\s+</g, '><');
|
||||
|
||||
return `
|
||||
<div class="docs-code" role="group">
|
||||
${highlightResult}
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
image({href, title, text}): string {
|
||||
return `
|
||||
<img src="${href}" alt="${text}" title="${title}" class="docs-image">
|
||||
`;
|
||||
},
|
||||
link(this: Renderer, {href, tokens}): string {
|
||||
return `<a href="${href}">${this.parser.parseInline(tokens)}</a>`;
|
||||
},
|
||||
list(this: Renderer, {items, ordered, start}) {
|
||||
if (ordered) {
|
||||
return `
|
||||
<ol class="docs-ordered-list">
|
||||
${items.map((item) => this.listitem(item)).join('')}
|
||||
</ol>
|
||||
`;
|
||||
}
|
||||
return `
|
||||
<ul class="docs-list">
|
||||
${items.map((item) => this.listitem(item)).join('')}
|
||||
</ul>
|
||||
`;
|
||||
},
|
||||
|
||||
table(this: Renderer, {header, rows}: Tokens.Table) {
|
||||
return `
|
||||
<div class="docs-table docs-scroll-track-transparent">
|
||||
<table>
|
||||
<thead>
|
||||
${this.tablerow({text: header.map((cell) => this.tablecell(cell)).join('')})}
|
||||
</thead>
|
||||
<tbody>
|
||||
${rows
|
||||
.map((row) => this.tablerow({text: row.map((cell) => this.tablecell(cell)).join('')}))
|
||||
.join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
`;
|
||||
},
|
||||
|
||||
heading(this: Renderer, {text, depth, tokens}: Tokens.Heading) {
|
||||
const id = text
|
||||
.toLowerCase()
|
||||
.replaceAll(' ', '-')
|
||||
.replace(/[^a-z0-9-]/g, '');
|
||||
|
||||
// Since we have a code transformer `addApiLinksToHtml` which adds anchors
|
||||
// to code blocks of known symbols, we add an additional `data-skip-anchor`
|
||||
// attribute that prevents the transformation. This is needed since nested
|
||||
// anchor tags are illegal and break the HTML.
|
||||
const textRenderer = new Renderer();
|
||||
textRenderer.codespan = ({text}) => `<code data-skip-anchor>${text}</code>`;
|
||||
const parsedText = this.parser.parseInline(tokens, textRenderer);
|
||||
|
||||
// The template matches templates/section-heading.tsx
|
||||
return `
|
||||
<h${depth} id="${id}" class="${SECTION_HEADING} ${SECTION_SUB_HEADING}">
|
||||
<a href="#${id}" aria-label="Link to ${text} section" tabIndex="-1">
|
||||
${parsedText}
|
||||
</a>
|
||||
</h${depth}>
|
||||
`;
|
||||
},
|
||||
};
|
||||
@@ -1,196 +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 * as blockC from './region-matchers/block-c.mjs';
|
||||
import * as html from './region-matchers/html.mjs';
|
||||
import * as inlineC from './region-matchers/inline-c.mjs';
|
||||
import * as inlineCOnly from './region-matchers/inline-c-only.mjs';
|
||||
import * as inlineHash from './region-matchers/inline-hash.mjs';
|
||||
import {FileType, removeEslintComments} from './remove-eslint-comments.mjs';
|
||||
|
||||
const WHOLE_FILE_REGION_NAME = '';
|
||||
const DEFAULT_PLASTER = '. . .';
|
||||
const REGION_MATCHERS = {
|
||||
ts: inlineC,
|
||||
js: inlineC,
|
||||
mjs: inlineCOnly,
|
||||
es6: inlineC,
|
||||
html: html,
|
||||
svg: html,
|
||||
css: blockC,
|
||||
conf: inlineHash,
|
||||
yaml: inlineHash,
|
||||
yml: inlineHash,
|
||||
sh: inlineHash,
|
||||
jade: inlineCOnly,
|
||||
pug: inlineCOnly,
|
||||
json: inlineCOnly,
|
||||
'json.annotated': inlineCOnly,
|
||||
};
|
||||
|
||||
interface Region {
|
||||
lines: string[];
|
||||
open?: boolean;
|
||||
ranges: {from: number; to?: number}[];
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: We assume that the tag defining the beginning and end of the region will be in different lines in each case.
|
||||
* For example, in HTML, we don't expect to have the following code on one line: <!-- docregion name -->content<!--enddocregion name-->
|
||||
*/
|
||||
export const regionParser = (contents: string, filePath: string) => {
|
||||
const fileType: FileType | undefined = filePath?.split('.').pop() as FileType;
|
||||
|
||||
if (!fileType) {
|
||||
throw new Error(`Incorrect file type for region parser: ${filePath}!`);
|
||||
}
|
||||
|
||||
const regionMatcher = REGION_MATCHERS[fileType];
|
||||
const openRegions: string[] = [];
|
||||
const regionMap: Record<string, Region> = {};
|
||||
|
||||
let countOfRegionLines = 0;
|
||||
|
||||
if (regionMatcher) {
|
||||
let plaster = regionMatcher.createPlasterComment(DEFAULT_PLASTER);
|
||||
const lines = removeEslintComments(contents, fileType)
|
||||
.split(/\r?\n/)
|
||||
.filter((line, index) => {
|
||||
const startRegion = line.match(regionMatcher.regionStartMatcher);
|
||||
const endRegion = line.match(regionMatcher.regionEndMatcher);
|
||||
const updatePlaster = line.match(regionMatcher.plasterMatcher);
|
||||
|
||||
// start region processing
|
||||
if (startRegion) {
|
||||
// open up the specified region
|
||||
handleStartRegion(
|
||||
startRegion,
|
||||
regionMap,
|
||||
index,
|
||||
countOfRegionLines,
|
||||
plaster,
|
||||
openRegions,
|
||||
);
|
||||
} else if (endRegion) {
|
||||
// end region processing
|
||||
handleEndRegion(openRegions, endRegion, regionMap, index, countOfRegionLines);
|
||||
} else if (updatePlaster) {
|
||||
// doc plaster processing
|
||||
const plasterString = updatePlaster[1].trim();
|
||||
plaster = plasterString ? regionMatcher.createPlasterComment(plasterString) : '';
|
||||
} else {
|
||||
// simple line of content processing
|
||||
openRegions.forEach((regionName) => regionMap[regionName].lines.push(line));
|
||||
// do not filter out this line from the content
|
||||
return true;
|
||||
}
|
||||
|
||||
// this line contained an annotation so let's filter it out
|
||||
countOfRegionLines++;
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!regionMap[WHOLE_FILE_REGION_NAME]) {
|
||||
regionMap[WHOLE_FILE_REGION_NAME] = {lines, ranges: [{from: 1, to: lines.length + 1}]};
|
||||
}
|
||||
|
||||
return {
|
||||
contents: lines.join('\n'),
|
||||
regionMap,
|
||||
totalLinesCount: lines.length,
|
||||
};
|
||||
} else {
|
||||
return {contents, regionMap, totalLinesCount: 0};
|
||||
}
|
||||
};
|
||||
|
||||
function handleStartRegion(
|
||||
startRegion: RegExpMatchArray,
|
||||
regionMap: Record<string, Region>,
|
||||
index: number,
|
||||
countOfRegionLines: number,
|
||||
plaster: string,
|
||||
openRegions: string[],
|
||||
) {
|
||||
const regionNames = getRegionNames(startRegion[1]);
|
||||
if (regionNames.length === 0) {
|
||||
regionNames.push(WHOLE_FILE_REGION_NAME);
|
||||
}
|
||||
|
||||
for (const regionName of regionNames) {
|
||||
const region = regionMap[regionName];
|
||||
if (region) {
|
||||
if (region.open) {
|
||||
throw new Error(`Tried to open a region, named "${regionName}", that is already open`);
|
||||
}
|
||||
|
||||
// Region is opened, set from range value.
|
||||
region.open = true;
|
||||
region.ranges.push({from: getFromRangeValue(index, countOfRegionLines)});
|
||||
|
||||
if (plaster) {
|
||||
// Use the same indent as the docregion marker
|
||||
const indent = startRegion[0].split(/[^ ]/, 1);
|
||||
region.lines.push(indent + plaster);
|
||||
}
|
||||
} else {
|
||||
regionMap[regionName] = {
|
||||
lines: [],
|
||||
open: true,
|
||||
ranges: [
|
||||
{
|
||||
from: getFromRangeValue(index, countOfRegionLines),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
openRegions.push(regionName);
|
||||
}
|
||||
}
|
||||
|
||||
function handleEndRegion(
|
||||
openRegions: string[],
|
||||
endRegion: RegExpMatchArray,
|
||||
regionMap: Record<string, Region>,
|
||||
index: number,
|
||||
countOfRegionLines: number,
|
||||
) {
|
||||
if (openRegions.length === 0) {
|
||||
throw new Error('Tried to close a region when none are open');
|
||||
}
|
||||
// close down the specified region (or most recent if no name is given)
|
||||
const regionNames = getRegionNames(endRegion[1]);
|
||||
if (regionNames.length === 0) {
|
||||
regionNames.push(openRegions[openRegions.length - 1]);
|
||||
}
|
||||
|
||||
for (const regionName of regionNames) {
|
||||
const region = regionMap[regionName];
|
||||
if (!region || !region.open) {
|
||||
throw new Error(`Tried to close a region, named "${regionName}", that is not open`);
|
||||
}
|
||||
|
||||
// Region is closed, we can define the last line number of the region
|
||||
region.open = false;
|
||||
region.ranges[region.ranges.length - 1].to = index - countOfRegionLines;
|
||||
removeLast(openRegions, regionName);
|
||||
}
|
||||
}
|
||||
|
||||
function getFromRangeValue(index: number, countOfRegionLines: number): number {
|
||||
return index - countOfRegionLines + 1;
|
||||
}
|
||||
|
||||
function getRegionNames(input: string): string[] {
|
||||
return input.trim() === '' ? [] : input.split(',').map((name) => name.trim());
|
||||
}
|
||||
|
||||
function removeLast(array: string[], item: string): void {
|
||||
const index = array.lastIndexOf(item);
|
||||
array.splice(index, 1);
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// This definition file is a temporary workaround until the toolchain is able to read d.mts definition files
|
||||
// TODO: delete this file when the toolchains supports .d.mts files
|
||||
declare module 'shiki' {
|
||||
function createHighlighter(params: {
|
||||
themes: any[];
|
||||
langs: string[];
|
||||
cssVariablePrefix?: string;
|
||||
defaultColor?: boolean;
|
||||
}): unknown;
|
||||
|
||||
type HighlighterGeneric<BundledLangKeys, BundledThemeKeys> = any;
|
||||
}
|
||||
@@ -6,41 +6,24 @@
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
let highlighter: any;
|
||||
import {HighlighterGeneric} from 'shiki';
|
||||
|
||||
export async function initHighlighter() {
|
||||
const {createHighlighter} = await import('shiki');
|
||||
highlighter = await createHighlighter({
|
||||
themes: ['github-light', 'github-dark'],
|
||||
langs: [
|
||||
'javascript',
|
||||
'typescript',
|
||||
'angular-html',
|
||||
'angular-ts',
|
||||
'shell',
|
||||
'html',
|
||||
'http',
|
||||
'json',
|
||||
'jsonc',
|
||||
'nginx',
|
||||
'markdown',
|
||||
'apache',
|
||||
],
|
||||
});
|
||||
/**
|
||||
* Singleton instance of the Shiki highlighter used during API page generation.
|
||||
*
|
||||
* TODO: Consider removing this singleton pattern if we find a better alternative.
|
||||
*/
|
||||
let highlighter: HighlighterGeneric<any, any>;
|
||||
export function setHighlighterInstance(instance: HighlighterGeneric<any, any>) {
|
||||
highlighter = instance;
|
||||
}
|
||||
|
||||
export function codeToHtml(code: string, language: string | undefined): string {
|
||||
const html = highlighter.codeToHtml(code, {
|
||||
lang: language ?? 'text',
|
||||
themes: {
|
||||
light: 'github-light',
|
||||
dark: 'github-dark',
|
||||
},
|
||||
cssVariablePrefix: '--shiki-',
|
||||
defaultColor: false,
|
||||
});
|
||||
|
||||
return html;
|
||||
export function getHighlighterInstance(): HighlighterGeneric<any, any> {
|
||||
if (!highlighter) {
|
||||
throw new Error(
|
||||
'Shiki highlighter has not been initialized. Make sure to call `setHighlighterInstance`.',
|
||||
);
|
||||
}
|
||||
return highlighter;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,38 +6,37 @@
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {getSymbolUrl as sharedGetSymbolUrl} from '../../shared/linking.mjs';
|
||||
|
||||
/**
|
||||
* API pages are generated each package at a time.
|
||||
* This allows to use a global context to store the symbols and their corresponding module names.
|
||||
* This allows us to use a global context to store the symbols and their corresponding module names.
|
||||
*/
|
||||
|
||||
let symbols = new Map<string, string>();
|
||||
let symbols: Record<string, string> = {};
|
||||
|
||||
// This is used to store the currently processed symbol (usually a class or an interface)
|
||||
let currentSymbol: string | undefined;
|
||||
|
||||
export function setSymbols(newSymbols: Map<string, string>): void {
|
||||
symbols = newSymbols;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the module name of a symbol.
|
||||
* eg: 'ApplicationRef' => 'core', 'FormControl' => 'forms'
|
||||
* Also supports class.member, 'NgZone.runOutsideAngular => 'core'
|
||||
*/
|
||||
export function getModuleName(symbol: string): string | undefined {
|
||||
const moduleName = symbols.get(symbol);
|
||||
return moduleName?.replace('@angular/', '');
|
||||
}
|
||||
|
||||
export function setCurrentSymbol(symbol: string): void {
|
||||
currentSymbol = symbol;
|
||||
}
|
||||
|
||||
export function getSymbols() {
|
||||
return symbols;
|
||||
}
|
||||
|
||||
export function getCurrentSymbol(): string | undefined {
|
||||
return currentSymbol;
|
||||
}
|
||||
|
||||
export function setSymbols(newSymbols: Record<string, string>): void {
|
||||
symbols = newSymbols;
|
||||
}
|
||||
|
||||
export function getSymbolUrl(symbol: string): string | undefined {
|
||||
return sharedGetSymbolUrl(symbol, symbols);
|
||||
}
|
||||
|
||||
export function unknownSymbolMessage(link: string, symbol: string): string {
|
||||
return `WARNING: {@link ${link}} is invalid, ${symbol} or ${currentSymbol}.${symbol} is unknown in this context`;
|
||||
}
|
||||
|
||||
@@ -20,9 +20,10 @@ import {SectionDescription} from './section-description';
|
||||
import {SectionUsageNotes} from './section-usage-notes';
|
||||
import {SectionApi} from './section-api';
|
||||
import {SectionHeading} from './section-heading';
|
||||
import {codeToHtml} from '../shiki/shiki.mjs';
|
||||
import {RawHtml} from './raw-html';
|
||||
import {DeprecationWarning} from './deprecation-warning';
|
||||
import {codeToHtml} from '../../../shared/shiki.mjs';
|
||||
import {getHighlighterInstance} from '../shiki/shiki.mjs';
|
||||
|
||||
/** Component to render a class API reference document. */
|
||||
export function ClassReference(entry: ClassEntryRenderable | PipeEntryRenderable) {
|
||||
@@ -33,7 +34,13 @@ export function ClassReference(entry: ClassEntryRenderable | PipeEntryRenderable
|
||||
<>
|
||||
<div className={SECTION_CONTAINER + ' docs-reference-api-section'}>
|
||||
<SectionHeading name="Pipe usage" />
|
||||
<RawHtml value={codeToHtml((entry as PipeEntry).usage, 'angular-html')} />
|
||||
<RawHtml
|
||||
value={codeToHtml(
|
||||
getHighlighterInstance(),
|
||||
(entry as PipeEntry).usage,
|
||||
'angular-html',
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -24,31 +24,28 @@ export function CliCard(props: {card: CliCardRenderable}) {
|
||||
{item.aliases?.map((alias) => (
|
||||
<div class="docs-reference-option-aliases">
|
||||
<span>Alias</span>
|
||||
<code>{alias} </code>
|
||||
<code>{alias}</code>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div dangerouslySetInnerHTML={{__html: item.description}}></div>
|
||||
</div>
|
||||
<div class="docs-reference-type-and-default">
|
||||
{/* Display the enum values if there are some, else the type expected for the option */}
|
||||
{/* Display the type expected for the option and the enum values if there are some. */}
|
||||
<span>Value Type</span>
|
||||
<code>{item.type}</code>
|
||||
{item.enum ? (
|
||||
<>
|
||||
<span>Allowed Values</span>
|
||||
<span>
|
||||
{item.enum.map((val, i, items) => (
|
||||
<>
|
||||
<code>{val}</code>
|
||||
{i < items.length - 1 && ', '}
|
||||
</>
|
||||
))}
|
||||
</span>
|
||||
{item.enum.map((val, i, items) => (
|
||||
<>
|
||||
<code>{val}</code>
|
||||
{i < items.length - 1 && ', '}
|
||||
</>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<span>Value Type</span>
|
||||
<code>{item.type}</code>
|
||||
</>
|
||||
<></>
|
||||
)}
|
||||
{/* Default Value */}
|
||||
{item.default !== undefined ? <span>Default</span> : <></>}
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
*/
|
||||
|
||||
import {h} from 'preact';
|
||||
import {getModuleName} from '../symbol-context.mjs';
|
||||
import {getLinkToModule} from '../transforms/url-transforms.mjs';
|
||||
import {getSymbolUrl} from '../symbol-context.mjs';
|
||||
|
||||
const symbolRegex = /([a-zA-Z_$][a-zA-Z_$0-9\.]*)/;
|
||||
|
||||
@@ -23,11 +22,8 @@ export function CodeSymbol(props: {code: string}) {
|
||||
// Every even index is a non-match when the regex has 1 capturing group
|
||||
if (index % 2 === 0) return rawSymbol;
|
||||
|
||||
let [symbol, subSymbol] = rawSymbol.split('.'); // Also takes care of methods, enum value etc.
|
||||
const moduleName = getModuleName(symbol);
|
||||
|
||||
if (moduleName) {
|
||||
const url = getLinkToModule(moduleName, symbol, subSymbol);
|
||||
const url = getSymbolUrl(rawSymbol);
|
||||
if (url) {
|
||||
return <a href={url}>{rawSymbol}</a>;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,11 +8,13 @@
|
||||
|
||||
import {h} from 'preact';
|
||||
import {RawHtml} from './raw-html';
|
||||
import {codeToHtml} from '../shiki/shiki.mjs';
|
||||
import {codeToHtml} from '../../../shared/shiki.mjs';
|
||||
import {get} from 'http';
|
||||
import {getHighlighterInstance} from '../shiki/shiki.mjs';
|
||||
|
||||
/** Component to render a header of the CLI page. */
|
||||
export function HighlightTypeScript(props: {code: string}) {
|
||||
const result = codeToHtml(props.code, 'typescript');
|
||||
const result = codeToHtml(getHighlighterInstance(), props.code, 'typescript');
|
||||
const withScrollTrack = result.replace(/^(<pre class="shiki)/, '$1 docs-mini-scroll-track');
|
||||
|
||||
return <RawHtml value={withScrollTrack} className="docs-code" />;
|
||||
|
||||
@@ -21,6 +21,7 @@ ts_project(
|
||||
"//adev:node_modules/@types/jsdom",
|
||||
"//adev:node_modules/jsdom",
|
||||
"//adev/shared-docs/pipeline/api-gen/rendering:render_api_to_html_lib",
|
||||
"//adev/shared-docs/pipeline/shared:shiki",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -9,18 +9,17 @@
|
||||
import {resolve} from 'path';
|
||||
import {readFile} from 'fs/promises';
|
||||
import {JSDOM} from 'jsdom';
|
||||
import {configureMarkedGlobally} from '../marked/configuration.mjs';
|
||||
import {getRenderable} from '../processing.mjs';
|
||||
import {renderEntry} from '../rendering.mjs';
|
||||
import {initHighlighter} from '../shiki/shiki.mjs';
|
||||
import {initHighlighter} from '../../../shared/shiki.mjs';
|
||||
import {setHighlighterInstance} from '../shiki/shiki.mjs';
|
||||
|
||||
describe('CLI docs to html', () => {
|
||||
let fragment: DocumentFragment;
|
||||
let entryJson: any;
|
||||
|
||||
beforeAll(async () => {
|
||||
await initHighlighter();
|
||||
await configureMarkedGlobally();
|
||||
setHighlighterInstance(await initHighlighter());
|
||||
|
||||
const entryContent = await readFile(resolve('./fake-cli-entries.json'), {
|
||||
encoding: 'utf-8',
|
||||
|
||||
@@ -1276,14 +1276,14 @@
|
||||
}
|
||||
],
|
||||
"symbols": [
|
||||
["AfterRenderPhase", "core"],
|
||||
["afterRender", "core"],
|
||||
["EmbeddedViewRef", "core"],
|
||||
["ChangeDetectionStrategy", "core"],
|
||||
["ChangeDetectorRef", "core"],
|
||||
["withNoHttpTransferCache", "platform-browser"],
|
||||
["withHttpTransferCacheOptions", "platform-browser"],
|
||||
["withI18nSupport", "platform-browser"],
|
||||
["withEventReplay", "platform-browser"]
|
||||
["AfterRenderPhase", "@angular/core"],
|
||||
["afterRender", "@angular/core"],
|
||||
["EmbeddedViewRef", "@angular/core"],
|
||||
["ChangeDetectionStrategy", "@angular/core"],
|
||||
["ChangeDetectorRef", "@angular/core"],
|
||||
["withNoHttpTransferCache", "@angular/platform-browser"],
|
||||
["withHttpTransferCacheOptions", "@angular/platform-browser"],
|
||||
["withI18nSupport", "@angular/platform-browser"],
|
||||
["withEventReplay", "@angular/platform-browser"]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
|
||||
import {readFile} from 'fs/promises';
|
||||
import {JSDOM} from 'jsdom';
|
||||
import {configureMarkedGlobally} from '../marked/configuration.mjs';
|
||||
import {getRenderable} from '../processing.mjs';
|
||||
import {renderEntry} from '../rendering.mjs';
|
||||
import {initHighlighter} from '../shiki/shiki.mjs';
|
||||
import {setSymbols} from '../symbol-context.mjs';
|
||||
import {resolve} from 'path';
|
||||
import {initHighlighter} from '../../../shared/shiki.mjs';
|
||||
import {setHighlighterInstance} from '../shiki/shiki.mjs';
|
||||
|
||||
// Note: The tests will probably break if the schema of the api extraction changes.
|
||||
// All entries in the fake-entries are extracted from Angular's api.
|
||||
@@ -24,14 +24,13 @@ describe('markdown to html', () => {
|
||||
const entries2 = new Map<string, string>();
|
||||
|
||||
beforeAll(async () => {
|
||||
await initHighlighter();
|
||||
await configureMarkedGlobally();
|
||||
setHighlighterInstance(await initHighlighter());
|
||||
|
||||
const entryContent = await readFile(resolve('./fake-entries.json'), {
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
const entryJson = JSON.parse(entryContent) as any;
|
||||
const symbols = new Map<string, string>([
|
||||
const symbols = Object.fromEntries([
|
||||
['AfterRenderPhase', 'core'],
|
||||
['afterRender', 'core'],
|
||||
['EmbeddedViewRef', 'core'],
|
||||
@@ -80,12 +79,14 @@ describe('markdown to html', () => {
|
||||
|
||||
// In the description
|
||||
const descriptionItem = entry.querySelector('.docs-reference-description')!;
|
||||
expect(descriptionItem.innerHTML).toContain('<a href="/api/core/afterRender">afterRender</a>');
|
||||
expect(descriptionItem.innerHTML).toContain(
|
||||
'<a href="/api/core/afterRender"><code>afterRender</code></a>',
|
||||
);
|
||||
|
||||
// In the card
|
||||
const cardItem = entry.querySelectorAll('.docs-reference-card-item')[1];
|
||||
expect(cardItem.innerHTML).toContain(
|
||||
'<a href="/api/core/AfterRenderPhase#MixedReadWrite">AfterRenderPhase.MixedReadWrite</a>',
|
||||
'<a href="/api/core/AfterRenderPhase#MixedReadWrite"><code>AfterRenderPhase.MixedReadWrite</code></a>',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
|
||||
import {readFile} from 'fs/promises';
|
||||
import {DocEntryRenderable} from '../entities/renderables.mjs';
|
||||
import {configureMarkedGlobally} from '../marked/configuration.mjs';
|
||||
import {getRenderable} from '../processing.mjs';
|
||||
import {initHighlighter} from '../shiki/shiki.mjs';
|
||||
import {setSymbols} from '../symbol-context.mjs';
|
||||
import {resolve} from 'path';
|
||||
import {initHighlighter} from '../../../shared/shiki.mjs';
|
||||
import {setHighlighterInstance} from '../shiki/shiki.mjs';
|
||||
|
||||
// Note: The tests will probably break if the schema of the api extraction changes.
|
||||
// All entries in the fake-entries are extracted from Angular's api.
|
||||
@@ -22,14 +22,13 @@ describe('renderable', () => {
|
||||
const entries = new Map<string, DocEntryRenderable>();
|
||||
|
||||
beforeAll(async () => {
|
||||
await initHighlighter();
|
||||
await configureMarkedGlobally();
|
||||
setHighlighterInstance(await initHighlighter());
|
||||
|
||||
const entryContent = await readFile(resolve('./fake-entries.json'), {
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
const entryJson = JSON.parse(entryContent) as any;
|
||||
const symbols = new Map<string, string>([
|
||||
const symbols = Object.fromEntries([
|
||||
['AfterRenderPhase', 'core'],
|
||||
['afterRender', 'core'],
|
||||
['EmbeddedViewRef', 'core'],
|
||||
|
||||
+40
-2
@@ -6,15 +6,17 @@
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {initHighlighter} from '../../../../shared/shiki.mjs';
|
||||
import {setHighlighterInstance} from '../../shiki/shiki.mjs';
|
||||
import {setCurrentSymbol, setSymbols} from '../../symbol-context.mjs';
|
||||
import {addHtmlAdditionalLinks} from '../../transforms/jsdoc-transforms.mjs';
|
||||
import {addHtmlAdditionalLinks, addHtmlDescription} from '../../transforms/jsdoc-transforms.mjs';
|
||||
|
||||
// @ts-ignore This compiles fine, but Webstorm doesn't like the ESM import in a CJS context.
|
||||
describe('jsdoc transforms', () => {
|
||||
it('should transform links', () => {
|
||||
setCurrentSymbol('Router');
|
||||
setSymbols(
|
||||
new Map([
|
||||
Object.fromEntries([
|
||||
['Route', 'test'],
|
||||
['Router', 'test'],
|
||||
['Router.someMethod', 'test'],
|
||||
@@ -151,4 +153,40 @@ describe('jsdoc transforms', () => {
|
||||
|
||||
expect(entryFn).toThrowError(/Forbidden relative link: cli\/build ng build/);
|
||||
});
|
||||
|
||||
it('should parse markdown in descriptions', async () => {
|
||||
setHighlighterInstance(await initHighlighter());
|
||||
|
||||
setSymbols(
|
||||
Object.fromEntries([
|
||||
['Route', 'test'],
|
||||
['Router', 'angular/router'],
|
||||
['Router.someMethod', 'test'],
|
||||
['Router.someMethodWithParenthesis', 'test'],
|
||||
['FormGroup', 'test'],
|
||||
['FormGroup.someMethod', 'test'],
|
||||
]),
|
||||
);
|
||||
|
||||
const entry = addHtmlDescription({
|
||||
description: `
|
||||
\`\`\`angular-ts
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
function setupRouter() {
|
||||
const router = inject(Router);
|
||||
}
|
||||
\`\`\`
|
||||
`,
|
||||
moduleName: 'test',
|
||||
});
|
||||
|
||||
// Should have some shiki variables (meaning the description was highlighted).
|
||||
expect(entry.htmlDescription).toContain('--shiki');
|
||||
|
||||
// Having docs-code means that the description was parsed and formatted correctly (by the shared marked renderer)
|
||||
expect(entry.htmlDescription).toContain('class="docs-code"');
|
||||
|
||||
expect(entry.htmlDescription).toContain('/api/angular/router/Router');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,13 +14,17 @@ import {
|
||||
CliCommandRenderable,
|
||||
CliOptionRenderable,
|
||||
} from '../entities/renderables.mjs';
|
||||
import {parseMarkdown} from '../../../shared/marked/parse.mjs';
|
||||
import {getHighlighterInstance} from '../shiki/shiki.mjs';
|
||||
|
||||
/** Given an unprocessed CLI entry, get the fully renderable CLI entry. */
|
||||
export function getCliRenderable(command: CliCommand): CliCommandRenderable {
|
||||
return {
|
||||
...command,
|
||||
subcommands: command.subcommands?.map((sub) => getCliRenderable(sub)),
|
||||
htmlDescription: marked.parse(command.longDescription ?? command.shortDescription) as string,
|
||||
htmlDescription: parseMarkdown(command.longDescription ?? command.shortDescription, {
|
||||
highlighter: getHighlighterInstance(),
|
||||
}),
|
||||
cards: getCliCardsRenderable(command),
|
||||
argumentsLabel: getArgumentsLabel(command),
|
||||
hasOptions: getOptions(command).length > 0,
|
||||
|
||||
@@ -34,16 +34,16 @@ import {
|
||||
} from '../entities/categorization.mjs';
|
||||
import {CodeLineRenderable} from '../entities/renderables.mjs';
|
||||
import {HasModuleName, HasRenderableToc} from '../entities/traits.mjs';
|
||||
import {getModuleName} from '../symbol-context.mjs';
|
||||
import {getSymbolUrl} from '../symbol-context.mjs';
|
||||
import {
|
||||
codeToHtml,
|
||||
getHighlighterInstance,
|
||||
insertParenthesesForDecoratorInShikiHtml,
|
||||
replaceKeywordFromShikiHtml,
|
||||
} from '../shiki/shiki.mjs';
|
||||
|
||||
import {filterLifecycleMethods, mergeGettersAndSetters} from './member-transforms.mjs';
|
||||
import {getLinkToModule} from './url-transforms.mjs';
|
||||
import {formatJs} from './format-code.mjs';
|
||||
import {codeToHtml} from '../../../shared/shiki.mjs';
|
||||
|
||||
const INDENT = ' ';
|
||||
const SPACE = ' ';
|
||||
@@ -85,7 +85,11 @@ export async function addRenderableCodeToc<T extends DocEntry & HasModuleName>(
|
||||
formattedCode = await formatJs(metadata.contents);
|
||||
}
|
||||
|
||||
let codeWithSyntaxHighlighting = codeToHtml(formattedCode ?? metadata?.contents, 'typescript');
|
||||
let codeWithSyntaxHighlighting = codeToHtml(
|
||||
getHighlighterInstance(),
|
||||
formattedCode ?? metadata?.contents,
|
||||
'typescript',
|
||||
);
|
||||
|
||||
if (isDecoratorEntry(entry)) {
|
||||
// Shiki requires a keyword for correct formating of Decorators
|
||||
@@ -525,14 +529,9 @@ export function addApiLinksToHtml(htmlString: string): string {
|
||||
// The captured content ==> vvvvvvvv
|
||||
/(?<!<a[^>]*>)(<(?:(?:span)|(?:code))(?!\sdata-skip-anchor)[^>]*>\s*)([^<]*?)(\s*<\/(?:span|code)>)/g,
|
||||
(type: string, span1: string, potentialSymbolName: string, span2: string) => {
|
||||
const [symbol, subSymbol] = potentialSymbolName.split(/(?:#|\.)/) as [string, string?];
|
||||
|
||||
// mySymbol() => mySymbol
|
||||
const symbolWithoutInvocation = symbol.replace(/\([^)]*\);?/g, '');
|
||||
const moduleName = getModuleName(symbolWithoutInvocation);
|
||||
|
||||
if (moduleName) {
|
||||
return `${span1}<a href="${getLinkToModule(moduleName, symbol, subSymbol)}">${potentialSymbolName}</a>${span2}`;
|
||||
const url = getSymbolUrl(potentialSymbolName);
|
||||
if (url) {
|
||||
return `${span1}<a href="${url}">${potentialSymbolName}</a>${span2}`;
|
||||
}
|
||||
|
||||
return type;
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
* found in the LICENSE file at https://angular.dev/license
|
||||
*/
|
||||
|
||||
import {marked} from 'marked';
|
||||
import {JsDocTagEntry} from '../entities.mjs';
|
||||
|
||||
import {getDeprecatedEntry, getTagSinceVersion} from '../entities/categorization.mjs';
|
||||
@@ -25,9 +24,15 @@ import {
|
||||
HasStableFlag,
|
||||
} from '../entities/traits.mjs';
|
||||
|
||||
import {getLinkToModule} from './url-transforms.mjs';
|
||||
import {addApiLinksToHtml} from './code-transforms.mjs';
|
||||
import {getCurrentSymbol, getModuleName, unknownSymbolMessage} from '../symbol-context.mjs';
|
||||
import {
|
||||
getCurrentSymbol,
|
||||
getSymbols,
|
||||
getSymbolUrl,
|
||||
unknownSymbolMessage,
|
||||
} from '../symbol-context.mjs';
|
||||
import {parseMarkdown} from '../../../shared/marked/parse.mjs';
|
||||
import {getHighlighterInstance} from '../shiki/shiki.mjs';
|
||||
|
||||
const JS_DOC_USAGE_NOTE_TAGS: Set<string> = new Set(['remarks', 'usageNotes', 'example']);
|
||||
export const JS_DOC_SEE_TAG = 'see';
|
||||
@@ -100,7 +105,11 @@ export function addHtmlUsageNotes<T extends HasJsDocTags>(entry: T): T & HasHtml
|
||||
|
||||
/** Given a markdown JsDoc text, gets the rendered HTML. */
|
||||
function getHtmlForJsDocText(text: string): string {
|
||||
const parsed = marked.parse(convertLinks(wrapExampleHtmlElementsWithCode(text))) as string;
|
||||
const mdToParse = convertLinks(wrapExampleHtmlElementsWithCode(text));
|
||||
const parsed = parseMarkdown(mdToParse, {
|
||||
apiEntries: getSymbols(),
|
||||
highlighter: getHighlighterInstance(),
|
||||
});
|
||||
return addApiLinksToHtml(parsed);
|
||||
}
|
||||
|
||||
@@ -141,7 +150,11 @@ function getHtmlAdditionalLinks<T extends HasJsDocTags>(entry: T): LinkEntryRend
|
||||
|
||||
if (linkMatch) {
|
||||
const link = linkMatch[1];
|
||||
const {url, label} = parseAtLink(link);
|
||||
const parsed = parseAtLink(link);
|
||||
if (!parsed) {
|
||||
return undefined;
|
||||
}
|
||||
const {url, label} = parsed;
|
||||
return {label, url};
|
||||
}
|
||||
|
||||
@@ -168,13 +181,17 @@ function wrapExampleHtmlElementsWithCode(text: string) {
|
||||
*/
|
||||
function convertLinks(text: string) {
|
||||
return text.replace(jsDoclinkRegexGlobal, (_, link) => {
|
||||
const {label, url} = parseAtLink(link);
|
||||
const parsed = parseAtLink(link);
|
||||
if (!parsed) {
|
||||
return `<code>${link}</code>`; // leave the link as-is if we can't parse it
|
||||
}
|
||||
const {label, url} = parsed;
|
||||
|
||||
return `<a href="${url}"><code>${label}</code></a>`;
|
||||
});
|
||||
}
|
||||
|
||||
function parseAtLink(link: string): {label: string; url: string} {
|
||||
function parseAtLink(link: string): {label: string; url: string} | undefined {
|
||||
// Because of microsoft/TypeScript/issues/59679
|
||||
// getTextOfJSDocComment introduces an extra space between the symbol and a trailing ()
|
||||
link = link.replace(/ \(\)$/, '');
|
||||
@@ -195,24 +212,18 @@ function parseAtLink(link: string): {label: string; url: string} {
|
||||
};
|
||||
}
|
||||
|
||||
let [symbol, subSymbol] = rawSymbol.replace(/\(\)$/, '').split(/(?:#|\.)/);
|
||||
|
||||
let moduleName = getModuleName(symbol);
|
||||
let url = getSymbolUrl(rawSymbol);
|
||||
const label = description ?? rawSymbol;
|
||||
|
||||
const currentSymbol = getCurrentSymbol();
|
||||
if (!url) {
|
||||
const currentSymbol = getCurrentSymbol();
|
||||
// 2nd attempt, try to get the module name in the context of the current symbol
|
||||
url = getSymbolUrl(`${currentSymbol}.${rawSymbol}`);
|
||||
|
||||
if (!moduleName) {
|
||||
// 2nd attemp, try to get the module name in the context of the current symbol
|
||||
moduleName = getModuleName(`${currentSymbol}.${symbol}`);
|
||||
|
||||
if (!moduleName || !currentSymbol) {
|
||||
throw unknownSymbolMessage(link, symbol);
|
||||
if (!url || !currentSymbol) {
|
||||
throw unknownSymbolMessage(link, rawSymbol);
|
||||
}
|
||||
|
||||
subSymbol = symbol;
|
||||
symbol = currentSymbol;
|
||||
}
|
||||
|
||||
return {label, url: getLinkToModule(moduleName, symbol, subSymbol)};
|
||||
return {label, url};
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user