Compare commits
141 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 60dbc2b39d | |||
| 293298a235 | |||
| 0be64c1628 | |||
| f50afb10c3 | |||
| 5328be6660 | |||
| cc1b0f4e8e | |||
| 635227b881 | |||
| 1081c51d1a | |||
| 79308d1668 | |||
| cb0379c8df | |||
| e8e1178e42 | |||
| 54a1fe05df | |||
| b334e29d59 | |||
| 6ca1911967 | |||
| 8af7d3a50b | |||
| 33c08e5aba | |||
| 181121dd9e | |||
| 5e45bb771b | |||
| 417cd71830 | |||
| 73969ae9d4 | |||
| cf61126ef3 | |||
| d2e5f23c38 | |||
| 1b9c807efa | |||
| 829a10937a | |||
| a75d9de874 | |||
| 7c1991048b | |||
| 52d672dd13 | |||
| d8db402833 | |||
| c5cdd8954c | |||
| 4876ed9510 | |||
| d90b6844a1 | |||
| 1a4d783bf8 | |||
| 6c7e1e61c6 | |||
| 17c59eb962 | |||
| 482335657b | |||
| 4feec728c0 | |||
| 76d8c7344e | |||
| a760470b20 | |||
| 5a63a475fb | |||
| c3da79710d | |||
| 879bd80b57 | |||
| b961075f92 | |||
| d01576b104 | |||
| de75fe0358 | |||
| 0e9c075cac | |||
| 922f950e45 | |||
| bec0179afe | |||
| 94bc3afc23 | |||
| dc80046132 | |||
| 6288f1c679 | |||
| 0482b6b4c7 | |||
| 9a56bd50f5 | |||
| 5901b0ca80 | |||
| 369157063c | |||
| 406448a09a | |||
| 9574edaa44 | |||
| e56c86f93c | |||
| d3309d7d9c | |||
| dbe2e3898b | |||
| 8cee78d25b | |||
| 97da5f7804 | |||
| 0472a92bc6 | |||
| e473b709bc | |||
| 70e2431b75 | |||
| 17199b9214 | |||
| 1f8a30dca9 | |||
| da906fdafc | |||
| 2b802587f2 | |||
| b71a32324e | |||
| 77a397b382 | |||
| 700c0520bb | |||
| 1b19d2d430 | |||
| 2d47329444 | |||
| 0dd441d08f | |||
| 10c5cdb49c | |||
| 8cad4e8cbe | |||
| edc1740d7e | |||
| e6ee6d25f9 | |||
| 0f7bbd0fc5 | |||
| 456f18be2e | |||
| db2f9a9561 | |||
| 4ff94aa46c | |||
| 6a07c0c2b2 | |||
| 1294b9a02a | |||
| 8735af08b9 | |||
| 87cae55e55 | |||
| bea7f33ad7 | |||
| 018f8266b3 | |||
| 47f79e78c0 | |||
| 142825d7b1 | |||
| f0a26001c5 | |||
| 6ea208ee90 | |||
| 9b424d7224 | |||
| 83932aa85e | |||
| 64f870c12b | |||
| d714e998d6 | |||
| eaff724b77 | |||
| 280a3a2d62 | |||
| 837016ccca | |||
| 5ae2bf4806 | |||
| ffb9b44333 | |||
| fd17b4e155 | |||
| 6235095011 | |||
| f86088f164 | |||
| 09a2f3d592 | |||
| 55c647bf90 | |||
| 1c9abb9ac8 | |||
| 81ccf5d102 | |||
| 5b927c094d | |||
| d269c88ae4 | |||
| fa315d4dad | |||
| 0bcaa0ebee | |||
| 484ae23bbf | |||
| 54340a9fff | |||
| ef32b5322e | |||
| 37d1f713a4 | |||
| fb189c6997 | |||
| 8180c39479 | |||
| 115ee88ba9 | |||
| 2787c50107 | |||
| d4247af8c5 | |||
| 53e743938f | |||
| 43ab781b70 | |||
| eee620aa00 | |||
| d888da4606 | |||
| 8bf842d595 | |||
| 30dccd4b77 | |||
| 4f808ad353 | |||
| e0c1f1e183 | |||
| 9104c62ab3 | |||
| 2258ac7a32 | |||
| ef39107ce5 | |||
| 126879e9d0 | |||
| cd242a11f5 | |||
| ad045efd4b | |||
| ba8e465974 | |||
| d9c0a16521 | |||
| 24bc0ed4f2 | |||
| fc0f47c53f | |||
| 781dbe2cc2 | |||
| 24a35366ca |
@@ -56,11 +56,6 @@ build --enable_runfiles
|
||||
build:release --workspace_status_command="yarn -s ng-dev release build-env-stamp --mode=release"
|
||||
build:release --stamp
|
||||
|
||||
# Building AIO against local Angular deps requires stamping
|
||||
# versions in Angular packages due to CLI version checks.
|
||||
build:aio_local_deps --stamp
|
||||
build:aio_local_deps --workspace_status_command="yarn -s --cwd aio local-workspace-status"
|
||||
|
||||
# Snapshots should also be stamped with version control information.
|
||||
build:snapshot-build --workspace_status_command="yarn -s ng-dev release build-env-stamp --mode=snapshot"
|
||||
build:snapshot-build --stamp
|
||||
@@ -74,14 +69,6 @@ build:snapshot-build --stamp
|
||||
##########################################################
|
||||
build --flag_alias=aio_build_config=//aio:flag_aio_build_config
|
||||
|
||||
####################################
|
||||
# AIO first party dep substitution #
|
||||
# Turn on with #
|
||||
# --config=aio_local_deps #
|
||||
####################################
|
||||
|
||||
build:aio_local_deps --//aio:flag_aio_local_deps
|
||||
|
||||
###############################
|
||||
# Output #
|
||||
###############################
|
||||
@@ -118,10 +105,6 @@ common:remote --jobs=200
|
||||
|
||||
build:remote --google_default_credentials
|
||||
|
||||
# Limit the number of test jobs for on an AIO local deps build. The example tests running
|
||||
# concurrently pushes the circleci executor RAM usage to its limits.
|
||||
test:aio_local_deps --jobs=24
|
||||
|
||||
# Force remote exeuctions to consider the entire run as linux
|
||||
build:remote --cpu=k8
|
||||
build:remote --host_cpu=k8
|
||||
|
||||
@@ -4,7 +4,7 @@ description: 'Installs the dependencies using Yarn'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4
|
||||
- uses: actions/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4
|
||||
with:
|
||||
path: |
|
||||
./node_modules/
|
||||
|
||||
@@ -21,16 +21,16 @@ jobs:
|
||||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'adev: preview'))
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Build adev to ensure it continues to work
|
||||
run: yarn bazel build --config=aio_local_deps //adev:build
|
||||
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@c83e99a12397014162531ca125c94549db55dd84
|
||||
run: yarn bazel build //adev:build
|
||||
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
with:
|
||||
workflow-artifact-name: 'adev-preview'
|
||||
pull-number: '${{github.event.pull_request.number}}'
|
||||
|
||||
@@ -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@c83e99a12397014162531ca125c94549db55dd84
|
||||
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
with:
|
||||
github-token: '${{secrets.GITHUB_TOKEN}}'
|
||||
workflow-artifact-name: 'adev-preview'
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
# This workflow builds the AIO previews for pull requests when a certain label is applied.
|
||||
# The actual deployment happens as part of a dedicated second workflow to avoid security
|
||||
# issues where the building would otherwise occur in an authorized context where secrets
|
||||
# could be leaked. More details can be found here:
|
||||
|
||||
# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/.
|
||||
|
||||
name: Build AIO app for preview deployment
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [synchronize, labeled]
|
||||
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
aio-build:
|
||||
runs-on: ubuntu-latest
|
||||
# We only want to build and deploy the AIO app if the `aio: preview` label has been
|
||||
# added, or if the label is already applied and new changes have been made in the PR.
|
||||
if: |
|
||||
(github.event.action == 'labeled' && github.event.label.name == 'aio: preview') ||
|
||||
(github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'aio: preview'))
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- uses: ./.github/actions/yarn-install
|
||||
|
||||
- uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
|
||||
with:
|
||||
bazelrc: ./.bazelrc.user
|
||||
|
||||
# Build the web package. Note: We run Bazel from a low-resource Github action container,
|
||||
# so we manually need to instruct Bazel to run more actions concurrently as by default
|
||||
# the number of concurrent actions is determined based on the host resources.
|
||||
- run: bazel build //aio:build --jobs=32 --announce_rc --verbose_failures
|
||||
|
||||
- uses: angular/dev-infra/github-actions/previews/pack-and-upload-artifact@c83e99a12397014162531ca125c94549db55dd84
|
||||
with:
|
||||
workflow-artifact-name: 'aio'
|
||||
pull-number: '${{github.event.pull_request.number}}'
|
||||
artifact-build-revision: '${{github.event.pull_request.head.sha}}'
|
||||
deploy-directory: './dist/bin/aio/build'
|
||||
@@ -1,44 +0,0 @@
|
||||
# This workflow runs whenever the AIO build workflow has completed. Deployment happens
|
||||
# as part of a dedicated second workflow to avoid security issues where the building would
|
||||
# otherwise occur in an authorized context where secrets could be leaked.
|
||||
#
|
||||
# More details can be found here:
|
||||
# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/.
|
||||
|
||||
name: Deploying AIO preview to Firebase
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ['Build AIO app for preview deployment']
|
||||
types: [completed]
|
||||
|
||||
permissions:
|
||||
# Needed in order to be able to comment on the pull request.
|
||||
pull-requests: write
|
||||
|
||||
env:
|
||||
PREVIEW_PROJECT: ng-dev-previews
|
||||
PREVIEW_SITE: ng-dev-previews-fw
|
||||
|
||||
jobs:
|
||||
aio-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
|
||||
|
||||
- name: Configure Firebase deploy target
|
||||
working-directory: aio/
|
||||
run: |
|
||||
# We can use `npx` as the Firebase deploy actions uses it too.
|
||||
npx -y firebase-tools@latest target:clear --project ${{env.PREVIEW_PROJECT}} hosting aio
|
||||
npx -y firebase-tools@latest target:apply --project ${{env.PREVIEW_PROJECT}} hosting aio ${{env.PREVIEW_SITE}}
|
||||
|
||||
- uses: angular/dev-infra/github-actions/previews/upload-artifacts-to-firebase@c83e99a12397014162531ca125c94549db55dd84
|
||||
with:
|
||||
github-token: '${{secrets.GITHUB_TOKEN}}'
|
||||
workflow-artifact-name: 'aio'
|
||||
firebase-config-dir: './aio/'
|
||||
firebase-public-dir: './aio/dist'
|
||||
firebase-project-id: '${{env.PREVIEW_PROJECT}}'
|
||||
firebase-service-key: '${{secrets.FIREBASE_PREVIEW_SERVICE_TOKEN}}'
|
||||
@@ -16,6 +16,6 @@ jobs:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: angular/dev-infra/github-actions/branch-manager@c83e99a12397014162531ca125c94549db55dd84
|
||||
- uses: angular/dev-infra/github-actions/branch-manager@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
exit: true
|
||||
|
||||
# Indicate that the benchmark command was received.
|
||||
- uses: peter-evans/create-or-update-comment@23ff15729ef2fc348714a3bb66d2f655ca9066f2 # v3
|
||||
- uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
|
||||
with:
|
||||
comment-id: ${{github.event.comment.id}}
|
||||
token: '${{secrets.BENCHMARK_POST_RESULTS_GITHUB_TOKEN}}'
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
|
||||
- uses: ./.github/actions/yarn-install
|
||||
|
||||
- uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
|
||||
- uses: angular/dev-infra/github-actions/bazel/configure-remote@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
with:
|
||||
bazelrc: ./.bazelrc.user
|
||||
|
||||
@@ -53,7 +53,7 @@ jobs:
|
||||
id: benchmark
|
||||
name: Running benchmark
|
||||
|
||||
- uses: peter-evans/create-or-update-comment@23ff15729ef2fc348714a3bb66d2f655ca9066f2 # v3
|
||||
- uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
|
||||
with:
|
||||
issue-number: ${{github.event.issue.number}}
|
||||
token: '${{secrets.BENCHMARK_POST_RESULTS_GITHUB_TOKEN}}'
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions: {}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
saucelabs:
|
||||
runs-on: ubuntu-latest-4core
|
||||
env:
|
||||
SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
|
||||
SAUCE_USERNAME: ${{ vars.SAUCE_USERNAME }}
|
||||
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
|
||||
with:
|
||||
cache-node-modules: true
|
||||
# Checking out the pull request commit is intended here as we need to run the changed code tests.
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
|
||||
- name: Starting Saucelabs tunnel service
|
||||
run: ./tools/saucelabs/sauce-service.sh run &
|
||||
# Build test fixtures for a test that rely on Bazel-generated fixtures. Note that disabling
|
||||
# specific tests which are reliant on such generated fixtures is not an option as SystemJS
|
||||
# in the Saucelabs legacy job always fetches referenced files, even if the imports would be
|
||||
# guarded by an check to skip in the Saucelabs legacy job. We should be good running such
|
||||
# test in all supported browsers on Saucelabs anyway until this job can be removed.
|
||||
- name: Preparing Bazel-generated fixtures required in legacy tests
|
||||
run: |
|
||||
yarn bazel build //packages/core/test:downleveled_es5_fixture //packages/common/locales
|
||||
# Needed for the ES5 downlevel reflector test in `packages/core/test/reflection`.
|
||||
mkdir -p dist/legacy-test-out/core/test/reflection/
|
||||
cp dist/bin/packages/core/test/reflection/es5_downleveled_inheritance_fixture.js \
|
||||
dist/legacy-test-out/core/test/reflection/es5_downleveled_inheritance_fixture.js
|
||||
# Locale files are needed for i18n tests running within Saucelabs. These are added
|
||||
# directly as sources so that the TypeScript compilation of `/packages/tsconfig.json`
|
||||
# can succeed. Note that the base locale and currencies files are checked-in, so
|
||||
# we do not need to re-generate those through Bazel.
|
||||
mkdir -p packages/common/locales/extra
|
||||
cp dist/bin/packages/common/locales/*.ts packages/common/locales
|
||||
cp dist/bin/packages/common/locales/extra/*.ts packages/common/locales/extra
|
||||
- name: Build bundle of tests to run on Saucelabs
|
||||
run: node tools/legacy-saucelabs/build-saucelabs-test-bundle.mjs
|
||||
- name: Wait and confirm Saucelabs tunnel has connected
|
||||
run: ./tools/saucelabs/sauce-service.sh ready-wait
|
||||
timeout-minutes: 3
|
||||
- name: Running tests on Saucelabs.
|
||||
run: KARMA_WEB_TEST_MODE=SL_REQUIRED yarn karma start ./karma-js.conf.js --single-run
|
||||
- name: Stop Saucelabs tunnel service
|
||||
run: ./tools/saucelabs/sauce-service.sh stop
|
||||
@@ -5,6 +5,7 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- '[0-9]+.[0-9]+.x'
|
||||
- 'remove-aio-stuff'
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
@@ -23,7 +24,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
with:
|
||||
cache-node-modules: true
|
||||
node-module-directories: |
|
||||
@@ -35,8 +36,6 @@ jobs:
|
||||
run: yarn install --frozen-lockfile --cwd aio
|
||||
- name: Check code lint
|
||||
run: yarn -s tslint
|
||||
- name: Check code lint in aio
|
||||
run: yarn -s --cwd aio lint
|
||||
- name: Check for circular dependencies
|
||||
run: yarn -s ts-circular-deps:check
|
||||
- name: Validate pull approve configuration
|
||||
@@ -60,13 +59,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Run unit tests
|
||||
@@ -78,118 +77,32 @@ jobs:
|
||||
runs-on: ubuntu-latest-4core
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Setup Bazel Remote Caching
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile --network-timeout 100000
|
||||
- name: Run CI tests for framework
|
||||
run: yarn test:ci
|
||||
|
||||
aio:
|
||||
runs-on: ubuntu-latest-4core
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
|
||||
with:
|
||||
cache-node-modules: true
|
||||
node-module-directories: |
|
||||
./aio/node_modules
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
|
||||
- name: Install node modules for aio
|
||||
run: yarn install --cwd aio --frozen-lockfile
|
||||
- name: Run AIO tests with upstream packages
|
||||
run: yarn --cwd aio test:ci
|
||||
- name: Check generated bundle sizes
|
||||
run: yarn --cwd aio payload-size
|
||||
|
||||
adev:
|
||||
runs-on:
|
||||
labels: ubuntu-latest-4core
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Build adev to ensure it continues to work
|
||||
run: yarn bazel test --config=aio_local_deps //adev:test
|
||||
|
||||
aio-local:
|
||||
runs-on:
|
||||
labels: ubuntu-latest-4core
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
|
||||
with:
|
||||
cache-node-modules: true
|
||||
node-module-directories: |
|
||||
./aio/node_modules
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
|
||||
- name: Install node modules
|
||||
run: yarn install --cwd aio --frozen-lockfile
|
||||
- name: Run AIO tests with local packages
|
||||
run: yarn --cwd aio test-local:ci
|
||||
- name: Check generated bundle sizes
|
||||
run: yarn --cwd aio payload-size aio-local
|
||||
|
||||
aio-deploy:
|
||||
needs: [aio]
|
||||
if: needs.aio.result == 'success' && github.event_name == 'push'
|
||||
runs-on:
|
||||
labels: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
|
||||
with:
|
||||
cache-node-modules: true
|
||||
node-module-directories: |
|
||||
./aio/node_modules
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
|
||||
- name: Install node modules for aio
|
||||
run: yarn install --cwd aio --frozen-lockfile
|
||||
- name: Set the stable branch environment variable
|
||||
id: latest_version
|
||||
run: |
|
||||
echo "CI_STABLE_BRANCH=$(npm info @angular/core dist-tags.latest | sed -r 's/^\s*([0-9]+\.[0-9]+)\.[0-9]+.*$/\1.x/')" >> $GITHUB_OUTPUT
|
||||
echo "CI_BRANCH=$(echo ${{ github.event.ref }} | cut -d '/' -f 3)" >> $GITHUB_OUTPUT
|
||||
- name: Deploy aio to production
|
||||
env:
|
||||
CI_AIO_MIN_PWA_SCORE: '95'
|
||||
CI_COMMIT: ${{ github.event.after }}
|
||||
CI_REPO_NAME: 'angular'
|
||||
CI_PULL_REQUEST: 'false'
|
||||
CI_REPO_OWNER: 'angular'
|
||||
CI_BUILD_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
||||
CI_STABLE_BRANCH: ${{ steps.latest_version.outputs.CI_STABLE_BRANCH }}
|
||||
CI_BRANCH: ${{ steps.latest_version.outputs.CI_BRANCH }}
|
||||
CI_SECRET_AIO_DEPLOY_FIREBASE_TOKEN: ${{ secrets.AIO_DEPLOY_TOKEN }}
|
||||
CI_SECRET_PAYLOAD_FIREBASE_TOKEN: ${{ secrets.AIO_PAYLOAD_TOKEN }}
|
||||
run: yarn --cwd aio deploy-production
|
||||
- name: Notify about failed deployment
|
||||
if: ${{ failure() }}
|
||||
uses: slackapi/slack-github-action@2a8087d4af6f83146a87539a70defe909fe6dbe6 # v=v1.24.0
|
||||
with:
|
||||
channel-id: 'C07DT5M6V,CKV1F72BG'
|
||||
slack-message: 'Deploy to aio job failed for ${{ github.base_ref }} branch failed on build ${{ github.event.after }}: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
||||
env:
|
||||
SLACK_BOT_TOKEN: ${{ secrets.ANGULAR_ROBOT_SLACK_TOKEN }}
|
||||
run: yarn bazel test //adev:test
|
||||
|
||||
publish-snapshots:
|
||||
if: github.event_name == 'push'
|
||||
@@ -197,13 +110,13 @@ jobs:
|
||||
labels: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- run: echo "https://${{secrets.SNAPSHOT_BUILDS_GITHUB_TOKEN}}:@github.com" > ${HOME}/.git_credentials
|
||||
@@ -215,7 +128,7 @@ jobs:
|
||||
labels: ubuntu-latest-4core
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
with:
|
||||
cache-node-modules: true
|
||||
node-module-directories: |
|
||||
@@ -223,9 +136,9 @@ jobs:
|
||||
./packages/zone.js/node_modules
|
||||
./packages/zone.js/test/typings/node_modules
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- run: |
|
||||
@@ -264,7 +177,7 @@ jobs:
|
||||
JOBS: 2
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
with:
|
||||
cache-node-modules: true
|
||||
# Checking out the pull request commit is intended here as we need to run the changed code tests.
|
||||
@@ -272,20 +185,66 @@ jobs:
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Setup Bazel Remote Caching
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Setup Saucelabs Variables
|
||||
uses: angular/dev-infra/github-actions/saucelabs@50bb71e51d3372ca2e359adb9069f89beeac4ae7
|
||||
- name: Set up Sauce Tunnel Daemon
|
||||
run: yarn bazel run //tools/saucelabs-daemon/background-service -- $JOBS &
|
||||
env:
|
||||
SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
|
||||
SAUCE_USERNAME: ${{ vars.SAUCE_USERNAME }}
|
||||
SAUCE_ACCESS_KEY: ${{ secrets.SAUCE_ACCESS_KEY }}
|
||||
- name: Run all saucelabs bazel tests
|
||||
run: |
|
||||
TESTS=$(./node_modules/.bin/bazelisk query --output label '(kind(karma_web_test, ...) intersect attr("tags", "saucelabs", ...)) except attr("tags", "fixme-saucelabs", ...)')
|
||||
yarn bazel test --config=saucelabs --jobs=$JOBS ${TESTS}
|
||||
|
||||
saucelabs:
|
||||
runs-on: ubuntu-latest-4core
|
||||
env:
|
||||
SAUCE_TUNNEL_IDENTIFIER: angular-framework-${{ github.run_number }}
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
with:
|
||||
cache-node-modules: true
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Setup Saucelabs Variables
|
||||
uses: angular/dev-infra/github-actions/saucelabs@50bb71e51d3372ca2e359adb9069f89beeac4ae7
|
||||
- name: Starting Saucelabs tunnel service
|
||||
run: ./tools/saucelabs/sauce-service.sh run &
|
||||
# Build test fixtures for a test that rely on Bazel-generated fixtures. Note that disabling
|
||||
# specific tests which are reliant on such generated fixtures is not an option as SystemJS
|
||||
# in the Saucelabs legacy job always fetches referenced files, even if the imports would be
|
||||
# guarded by an check to skip in the Saucelabs legacy job. We should be good running such
|
||||
# test in all supported browsers on Saucelabs anyway until this job can be removed.
|
||||
- name: Preparing Bazel-generated fixtures required in legacy tests
|
||||
run: |
|
||||
yarn bazel build //packages/core/test:downleveled_es5_fixture //packages/common/locales
|
||||
# Needed for the ES5 downlevel reflector test in `packages/core/test/reflection`.
|
||||
mkdir -p dist/legacy-test-out/core/test/reflection/
|
||||
cp dist/bin/packages/core/test/reflection/es5_downleveled_inheritance_fixture.js \
|
||||
dist/legacy-test-out/core/test/reflection/es5_downleveled_inheritance_fixture.js
|
||||
# Locale files are needed for i18n tests running within Saucelabs. These are added
|
||||
# directly as sources so that the TypeScript compilation of `/packages/tsconfig.json`
|
||||
# can succeed. Note that the base locale and currencies files are checked-in, so
|
||||
# we do not need to re-generate those through Bazel.
|
||||
mkdir -p packages/common/locales/extra
|
||||
cp dist/bin/packages/common/locales/*.ts packages/common/locales
|
||||
cp dist/bin/packages/common/locales/extra/*.ts packages/common/locales/extra
|
||||
- name: Build bundle of tests to run on Saucelabs
|
||||
run: node tools/legacy-saucelabs/build-saucelabs-test-bundle.mjs
|
||||
- name: Wait and confirm Saucelabs tunnel has connected
|
||||
run: ./tools/saucelabs/sauce-service.sh ready-wait
|
||||
timeout-minutes: 3
|
||||
- name: Running tests on Saucelabs.
|
||||
run: KARMA_WEB_TEST_MODE=SL_REQUIRED yarn karma start ./karma-js.conf.js --single-run
|
||||
- name: Stop Saucelabs tunnel service
|
||||
run: ./tools/saucelabs/sauce-service.sh stop
|
||||
|
||||
adev-deploy:
|
||||
needs: [adev]
|
||||
if: needs.adev.result == 'success' && github.event_name == 'push' && github.ref_name == 'main'
|
||||
@@ -295,15 +254,15 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Install node modules
|
||||
run: yarn install --frozen-lockfile
|
||||
- name: Build adev to ensure it continues to work
|
||||
run: yarn bazel build --config=aio_local_deps //adev:build
|
||||
run: yarn bazel build //adev:build
|
||||
- name: Move generated project files into deployment directory
|
||||
run: cp -r dist/bin/adev/build adev/build
|
||||
- name: Setup firebase context
|
||||
|
||||
@@ -13,13 +13,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- uses: angular/dev-infra/github-actions/commit-message-based-labels@c83e99a12397014162531ca125c94549db55dd84
|
||||
- uses: angular/dev-infra/github-actions/commit-message-based-labels@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
post_approval_changes:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- uses: angular/dev-infra/github-actions/post-approval-changes@c83e99a12397014162531ca125c94549db55dd84
|
||||
- uses: angular/dev-infra/github-actions/post-approval-changes@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- uses: angular/dev-infra/github-actions/google-internal-tests@c83e99a12397014162531ca125c94549db55dd84
|
||||
- uses: angular/dev-infra/github-actions/google-internal-tests@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
with:
|
||||
run-tests-guide-url: http://go/angular-g3sync-start
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -9,6 +9,6 @@ jobs:
|
||||
status:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: angular/dev-infra/github-actions/unified-status-check@c83e99a12397014162531ca125c94549db55dd84
|
||||
- uses: angular/dev-infra/github-actions/unified-status-check@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
with:
|
||||
angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }}
|
||||
|
||||
@@ -22,15 +22,15 @@ jobs:
|
||||
version: ['https://next.angular.io/', 'https://angular.io/']
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
with:
|
||||
cache-node-modules: true
|
||||
node-module-directories: |
|
||||
./aio/node_modules
|
||||
- name: Setup Bazel
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/bazel/setup@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Setup Bazel RBE
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/bazel/configure-remote@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
- name: Install node modules in aio
|
||||
run: yarn install --frozen-lockfile --cwd aio
|
||||
- name: Run basic e2e and deployment config tests.
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Initialize environment
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@5774b71c01a55c4c998f858ee37d3b77ae704c31
|
||||
with:
|
||||
cache-node-modules: true
|
||||
node-module-directories: |
|
||||
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
env:
|
||||
ANGULAR_CLI_BUILDS_READONLY_GITHUB_TOKEN: ${{ secrets.ANGULAR_CLI_BUILDS_READONLY_GITHUB_TOKEN }}
|
||||
- name: Create a PR (if necessary)
|
||||
uses: angular/dev-infra/github-actions/create-pr-for-changes@c83e99a12397014162531ca125c94549db55dd84
|
||||
uses: angular/dev-infra/github-actions/create-pr-for-changes@1421ff04811ecb45812930c5d1f61d912880e75f
|
||||
with:
|
||||
branch-prefix: update-cli-help
|
||||
pr-title: 'docs: update Angular CLI help [${{github.ref_name}}]'
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
# Update the `events.json` file that powers the [AIO events page](https://angular.io/events) (if
|
||||
# necessary) and create a pull request.
|
||||
#
|
||||
# For more details on the overall process, see
|
||||
# [aio/scripts/generate-events/README.md](../../aio/scripts/generate-events/README.md).
|
||||
|
||||
name: Update AIO events
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs: {}
|
||||
schedule:
|
||||
# Run every day at 15:00.
|
||||
- cron: '0 15 * * *'
|
||||
|
||||
# Declare default permissions as read only.
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update_events:
|
||||
name: Update `events.json` (if necessary)
|
||||
if: github.repository == 'angular/angular'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
with:
|
||||
# Setting `persist-credentials: false` prevents the github-action account from being the
|
||||
# account that is attempted to be used for authentication, instead the remote is set to
|
||||
# an authenticated URL.
|
||||
persist-credentials: false
|
||||
# This is needed as otherwise the PR creation will fail with `shallow update not allowed` when the forked branch is not in sync.
|
||||
fetch-depth: 0
|
||||
- name: Generate `events.json`
|
||||
run: node aio/scripts/generate-events/index.mjs --ignore-invalid-dates
|
||||
- name: Create a PR (if necessary)
|
||||
uses: angular/dev-infra/github-actions/create-pr-for-changes@c83e99a12397014162531ca125c94549db55dd84
|
||||
with:
|
||||
branch-prefix: docs-update-events
|
||||
pr-title: 'docs: update events'
|
||||
pr-description: |
|
||||
Generated `events.json` with the latest events retrieved from the Firebase DB.
|
||||
pr-labels: |
|
||||
action: review
|
||||
area: docs
|
||||
target: patch
|
||||
angular-robot-token: ${{ secrets.ANGULAR_ROBOT_ACCESS_TOKEN }}
|
||||
@@ -5,12 +5,7 @@
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": [
|
||||
"BazelBuild.vscode-bazel",
|
||||
"gkalpak.aio-docs-utils",
|
||||
"ms-vscode.vscode-typescript-tslint-plugin",
|
||||
"xaver.clang-format",
|
||||
// The following extensions are useful when working on angular.io (i.e. inside the `aio/` directory).
|
||||
//"angular.ng-template",
|
||||
//"dbaeumer.vscode-eslint",
|
||||
//"errata-ai.vale-server",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,3 +1,117 @@
|
||||
<a name="18.0.0-next.0"></a>
|
||||
# 18.0.0-next.0 (2024-03-14)
|
||||
## Breaking Changes
|
||||
### core
|
||||
- `OnPush` views at the root of the application need to
|
||||
be marked dirty for their host bindings to refresh. Previously, the host
|
||||
bindings were refreshed for all root views without respecting the
|
||||
`OnPush` change detection strategy.
|
||||
- The `ComponentFixture` `autoDetect` feature will no
|
||||
longer refresh the component's host view when the component is `OnPush`
|
||||
and not marked dirty. This exposes existing issues in components which
|
||||
claim to be `OnPush` but do not correctly call `markForCheck` when they
|
||||
need to be refreshed. If this change causes test failures, the easiest
|
||||
fix is to change the component to `ChangeDetectionStrategy.Default`.
|
||||
- When Angular runs change detection, it will continue to
|
||||
refresh any views attached to `ApplicationRef` that are still marked for
|
||||
check after one round completes. In rare cases, this can result in infinite
|
||||
loops when certain patterns continue to mark views for check using
|
||||
`ChangeDetectorRef.detectChanges`. This will be surfaced as a runtime
|
||||
error with the `NG0103` code.
|
||||
- The `ComponentFixture.autoDetect` feature now executes
|
||||
change detection for the fixture within `ApplicationRef.tick`. This more
|
||||
closely matches the behavior of how a component would refresh in
|
||||
production. The order of component refresh in tests may be slightly
|
||||
affected as a result, especially when dealing with additional components
|
||||
attached to the application, such as dialogs. Tests sensitive to this
|
||||
type of change (such as screenshot tests) may need to be updated.
|
||||
Concretely, this change means that the component will refresh _before_
|
||||
additional views attached to `ApplicationRef` (i.e. dialog components).
|
||||
Prior to this change, the fixture component would refresh _after_ other
|
||||
views attached to the application.
|
||||
- The exact timing of change detection execution when
|
||||
using event or run coalescing with `NgZone` is now the first of either
|
||||
`setTimeout` or `requestAnimationFrame`. Code which relies on this
|
||||
timing (usually by accident) will need to be adjusted. If a callback
|
||||
needs to execute after change detection, we recommend `afterNextRender`
|
||||
instead of something like `setTimeout`.
|
||||
- Newly created and views marked for check and reattached
|
||||
during change detection are now guaranteed to be refreshed in that same
|
||||
change detection cycle. Previously, if they were attached at a location
|
||||
in the view tree that was already checked, they would either throw
|
||||
`ExpressionChangedAfterItHasBeenCheckedError` or not be refreshed until
|
||||
some future round of change detection. In rare circumstances, this
|
||||
correction can cause issues. We identified one instance that relied on
|
||||
the previous behavior by reading a value on initialization which was
|
||||
queued to be updated in a microtask instead of being available in the
|
||||
current change detection round. The component only read this value during
|
||||
initialization and did not read it again after the microtask updated it.
|
||||
### router
|
||||
- Guards can now return `RedirectCommand` for redirects
|
||||
in addition to `UrlTree`. Code which expects only `boolean` or `UrlTree`
|
||||
values in `Route` types will need to be adjusted.
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [9b424d7224](https://github.com/angular/angular/commit/9b424d7224db46edb16c81979c7e231d5e3db5e9) | fix | preserve original reference to non-deferrable dependency ([#54759](https://github.com/angular/angular/pull/54759)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [64f870c12b](https://github.com/angular/angular/commit/64f870c12bae1ad66509f0d65f8d3e051aae6eaa) | fix | `ApplicationRef.tick` should respect OnPush for host bindings ([#53718](https://github.com/angular/angular/pull/53718)) ([#53718](https://github.com/angular/angular/pull/53718)) |
|
||||
| [8cad4e8cbe](https://github.com/angular/angular/commit/8cad4e8cbe2baf20dae7b7ef1f4253a4940cbba0) | fix | `ComponentFixture` `autoDetect` respects `OnPush` flag of host view ([#54824](https://github.com/angular/angular/pull/54824)) |
|
||||
| [ba8e465974](https://github.com/angular/angular/commit/ba8e46597435a827670f10b971b2c58f7033b180) | fix | Change Detection will continue to refresh views while marked for check ([#54734](https://github.com/angular/angular/pull/54734)) |
|
||||
| [24bc0ed4f2](https://github.com/angular/angular/commit/24bc0ed4f2de47bd998338d73cba394fb45dd497) | fix | ComponentFixture autodetect should detect changes within ApplicationRef.tick ([#54733](https://github.com/angular/angular/pull/54733)) |
|
||||
| [10c5cdb49c](https://github.com/angular/angular/commit/10c5cdb49c51c95086febd37f4d88a9b944d7e1c) | fix | ensure change detection runs in a reasonable timeframe with zone coalescing ([#54578](https://github.com/angular/angular/pull/54578)) |
|
||||
| [ad045efd4b](https://github.com/angular/angular/commit/ad045efd4b1565e01c14399998143538ebfbfd99) | fix | Ensure views marked for check are refreshed during change detection ([#54735](https://github.com/angular/angular/pull/54735)) |
|
||||
| [700c0520bb](https://github.com/angular/angular/commit/700c0520bb638952ba41a8d8260cf12afb078c0e) | fix | Update ApplicationRef.tick loop to only throw in dev mode ([#54848](https://github.com/angular/angular/pull/54848)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [8735af08b9](https://github.com/angular/angular/commit/8735af08b976b30cf236a83f9e8b64b5ff62e9f3) | feat | Add ability to return `UrlTree` with `NavigationBehaviorOptions` from guards ([#45023](https://github.com/angular/angular/pull/45023)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.3.0"></a>
|
||||
# 17.3.0 (2024-03-13)
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [1a6beae8a2](https://github.com/angular/angular/commit/1a6beae8a2bdcff27d4c1e402f98246a52247906) | feat | Enable template pipeline by default. ([#54571](https://github.com/angular/angular/pull/54571)) |
|
||||
| [f386a04c9d](https://github.com/angular/angular/commit/f386a04c9ddc2951c8105d5cafcce7b4bedea569) | fix | handle two-way bindings to signal-based template variables in instruction generation ([#54714](https://github.com/angular/angular/pull/54714)) |
|
||||
| [1f129f114e](https://github.com/angular/angular/commit/1f129f114edb21137d74471dd6b652848d2ffb68) | fix | not catching for loop empty tracking expressions ([#54772](https://github.com/angular/angular/pull/54772)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [12dc4d074e](https://github.com/angular/angular/commit/12dc4d074e63edaff626003ad6136a8d122b2ba6) | fix | account for as expression in docs extraction ([#54414](https://github.com/angular/angular/pull/54414)) |
|
||||
| [da7fbb40f0](https://github.com/angular/angular/commit/da7fbb40f06e6e37504f69e7b335f8219f424de2) | fix | detect when the linker is working in unpublished angular and widen supported versions ([#54439](https://github.com/angular/angular/pull/54439)) |
|
||||
| [492e03f699](https://github.com/angular/angular/commit/492e03f699ca95df4cf854885253f63a5a1fb5c1) | fix | flag two-way bindings to non-signal values in templates ([#54714](https://github.com/angular/angular/pull/54714)) |
|
||||
| [5afa4f0ec1](https://github.com/angular/angular/commit/5afa4f0ec1b64b88ef875d48bd143e0f36e0a955) | fix | support `ModuleWithProviders` literal detection with `typeof` ([#54650](https://github.com/angular/angular/pull/54650)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [331b16efd2](https://github.com/angular/angular/commit/331b16efd2f5af876e6dc0ad2474ee7a87b00de5) | feat | add API to inject attributes on the host node ([#54604](https://github.com/angular/angular/pull/54604)) |
|
||||
| [fb540e169a](https://github.com/angular/angular/commit/fb540e169a78a61f38d611f538eea8fdb0971f1d) | feat | add migration for invalid two-way bindings ([#54630](https://github.com/angular/angular/pull/54630)) |
|
||||
| [c687b8f453](https://github.com/angular/angular/commit/c687b8f4531252cd1c3dfbb9a7bd42bdbe666a36) | feat | expose new `output()` API ([#54650](https://github.com/angular/angular/pull/54650)) |
|
||||
| [c809069f21](https://github.com/angular/angular/commit/c809069f213244afd0e2d803a6a43510b218e6f5) | feat | introduce `outputFromObservable()` interop function ([#54650](https://github.com/angular/angular/pull/54650)) |
|
||||
| [aff65fd1f4](https://github.com/angular/angular/commit/aff65fd1f4a61ed76a6f9b623852f197eb3500e4) | feat | introduce `outputToObservable` interop helper ([#54650](https://github.com/angular/angular/pull/54650)) |
|
||||
| [974958913c](https://github.com/angular/angular/commit/974958913ca632971f878a045537472f2c99c665) | feat | support TypeScript 5.4 ([#54414](https://github.com/angular/angular/pull/54414)) |
|
||||
| [39a50f9a8d](https://github.com/angular/angular/commit/39a50f9a8df5afc4968d18924f3d9d7d6b649d3a) | fix | ensure all initializer functions run in an injection context ([#54761](https://github.com/angular/angular/pull/54761)) |
|
||||
| [243ccce624](https://github.com/angular/angular/commit/243ccce62475ae03a2e727d2b3cb2d51a595c4a7) | fix | exclude class attribute intended for projection matching from directive matching ([#54800](https://github.com/angular/angular/pull/54800)) |
|
||||
| [2909e9817d](https://github.com/angular/angular/commit/2909e9817daf69b6478d7d09229491a9a48cff16) | fix | prevent infinite loops in clobbered elements check ([#54425](https://github.com/angular/angular/pull/54425)) |
|
||||
| [7243c704cf](https://github.com/angular/angular/commit/7243c704cf8a4986fae419793027458e142658f0) | fix | return a readonly signal on `asReadonly`. ([#54706](https://github.com/angular/angular/pull/54706)) |
|
||||
| [bb35414a38](https://github.com/angular/angular/commit/bb35414a38f8a8447d03c66dc5bf60b84409f9e3) | perf | speed up retrieval of `DestroyRef` in `EventEmitter` ([#54748](https://github.com/angular/angular/pull/54748)) |
|
||||
### http
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [8d37ed035c](https://github.com/angular/angular/commit/8d37ed035c9e9796ba9a7b1f055404ea220dbb3b) | fix | exclude caching for authenticated HTTP requests ([#54746](https://github.com/angular/angular/pull/54746)) |
|
||||
### router
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [c1c7384e02](https://github.com/angular/angular/commit/c1c7384e02becc623c6a42985f7178ca98137264) | feat | Add reusable types for router guards ([#54580](https://github.com/angular/angular/pull/54580)) |
|
||||
| [7225485311](https://github.com/angular/angular/commit/722548531108e247660ebe6966b99b57d510b615) | fix | Navigations triggered by cancellation events should cancel previous navigation ([#54710](https://github.com/angular/angular/pull/54710)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.2.4"></a>
|
||||
# 17.2.4 (2024-03-06)
|
||||
### compiler-cli
|
||||
@@ -38,46 +152,6 @@
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.3.0-next.1"></a>
|
||||
# 17.3.0-next.1 (2024-02-27)
|
||||
### common
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [707bfc9b32](https://github.com/angular/angular/commit/707bfc9b326f321f302dccfdfebef7380c914eb5) | perf | `AsyncPipe` should not call `markForCheck` on subscription ([#54554](https://github.com/angular/angular/pull/54554)) |
|
||||
### compiler
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [1a6beae8a2](https://github.com/angular/angular/commit/1a6beae8a2bdcff27d4c1e402f98246a52247906) | feat | Enable template pipeline by default. ([#54571](https://github.com/angular/angular/pull/54571)) |
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [12dc4d074e](https://github.com/angular/angular/commit/12dc4d074e63edaff626003ad6136a8d122b2ba6) | fix | account for as expression in docs extraction ([#54414](https://github.com/angular/angular/pull/54414)) |
|
||||
| [f578889ca2](https://github.com/angular/angular/commit/f578889ca2dbb22f2e050d4c4af6aa2d597bf70d) | fix | catch function instance properties in interpolated signal diagnostic ([#54325](https://github.com/angular/angular/pull/54325)) |
|
||||
| [f04ecc0cda](https://github.com/angular/angular/commit/f04ecc0cdaeb3a292a748b1ccc94ce70a573fc79) | fix | identify aliased initializer functions ([#54480](https://github.com/angular/angular/pull/54480)) |
|
||||
| [f5c566c079](https://github.com/angular/angular/commit/f5c566c0793eacf9ca146c8a6b8da15b0e8f4c4d) | fix | identify aliased initializer functions ([#54609](https://github.com/angular/angular/pull/54609)) |
|
||||
### core
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [331b16efd2](https://github.com/angular/angular/commit/331b16efd2f5af876e6dc0ad2474ee7a87b00de5) | feat | add API to inject attributes on the host node ([#54604](https://github.com/angular/angular/pull/54604)) |
|
||||
| [974958913c](https://github.com/angular/angular/commit/974958913ca632971f878a045537472f2c99c665) | feat | support TypeScript 5.4 ([#54414](https://github.com/angular/angular/pull/54414)) |
|
||||
| [dcb9deb363](https://github.com/angular/angular/commit/dcb9deb3631ff4e839f738f2fb97ca6b894256ef) | fix | collect providers from NgModules while rendering `@defer` block ([#52881](https://github.com/angular/angular/pull/52881)) |
|
||||
| [ff40c9f762](https://github.com/angular/angular/commit/ff40c9f762b2c14870ac2859201ece9660087e79) | fix | fix typo in injectors.svg file ([#54596](https://github.com/angular/angular/pull/54596)) |
|
||||
### migrations
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [71e0c7df69](https://github.com/angular/angular/commit/71e0c7df695f274a54d5ffb9dfc0587d06d3a953) | fix | resolve infinite loop for a single line element with a long tag name and angle bracket on a new line ([#54588](https://github.com/angular/angular/pull/54588)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.3.0-next.0"></a>
|
||||
# 17.3.0-next.0 (2024-02-21)
|
||||
### compiler-cli
|
||||
| Commit | Type | Description |
|
||||
| -- | -- | -- |
|
||||
| [da7fbb40f0](https://github.com/angular/angular/commit/da7fbb40f06e6e37504f69e7b335f8219f424de2) | fix | detect when the linker is working in unpublished angular and widen supported versions ([#54439](https://github.com/angular/angular/pull/54439)) |
|
||||
|
||||
<!-- CHANGELOG SPLIT MARKER -->
|
||||
|
||||
<a name="17.2.2"></a>
|
||||
# 17.2.2 (2024-02-21)
|
||||
### common
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2023 Google LLC. https://angular.io/license
|
||||
Copyright (c) 2010-2024 Google LLC. https://angular.io/license
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -2,7 +2,6 @@ workspace(
|
||||
name = "angular",
|
||||
managed_directories = {
|
||||
"@npm": ["node_modules"],
|
||||
"@aio_npm": ["aio/node_modules"],
|
||||
},
|
||||
)
|
||||
|
||||
@@ -109,30 +108,6 @@ yarn_install(
|
||||
yarn_lock = "//:yarn.lock",
|
||||
)
|
||||
|
||||
yarn_install(
|
||||
name = "aio_npm",
|
||||
# Note that we add the postinstall scripts here so that the dependencies are re-installed
|
||||
# when the postinstall patches are modified.
|
||||
data = [
|
||||
YARN_LABEL,
|
||||
"//:.yarnrc",
|
||||
"//:tools/npm-patches/@bazel+jasmine+5.8.1.patch",
|
||||
"//aio:tools/cli-patches/bazel-architect-output.patch",
|
||||
"//aio:tools/cli-patches/patch.js",
|
||||
],
|
||||
# Currently disabled due to:
|
||||
# 1. Missing Windows support currently.
|
||||
# 2. Incompatibilites with the `ts_library` rule.
|
||||
exports_directories_only = False,
|
||||
manual_build_file_contents = npm_package_archives(),
|
||||
package_json = "//aio:package.json",
|
||||
# We prefer to symlink the `node_modules` to only maintain a single install.
|
||||
# See https://github.com/angular/dev-infra/pull/446#issuecomment-1059820287 for details.
|
||||
symlink_node_modules = True,
|
||||
yarn = YARN_LABEL,
|
||||
yarn_lock = "//aio:yarn.lock",
|
||||
)
|
||||
|
||||
yarn_install(
|
||||
name = "aio_example_deps",
|
||||
# Rename the default js_library target from "node_modules" as this obscures the
|
||||
@@ -206,10 +181,10 @@ cldr_xml_data_repository(
|
||||
# sass rules
|
||||
http_archive(
|
||||
name = "io_bazel_rules_sass",
|
||||
sha256 = "974f5e14c54d300ca63a67910ebfcea6d838d8811fefc61184f2872107152705",
|
||||
strip_prefix = "rules_sass-888dd9c02709e228b60a8b3e51e2b16b2ca5279d",
|
||||
sha256 = "5633816e996a79c77f44306f6228b9ef18fc2ecb412d63b4e314a132225facba",
|
||||
strip_prefix = "rules_sass-79bd239ce77c101ea1aed575678020bd2999f17d",
|
||||
urls = [
|
||||
"https://github.com/bazelbuild/rules_sass/archive/888dd9c02709e228b60a8b3e51e2b16b2ca5279d.zip",
|
||||
"https://github.com/bazelbuild/rules_sass/archive/79bd239ce77c101ea1aed575678020bd2999f17d.zip",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//:packages.bzl", "link_packages")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag")
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "copy_to_bin")
|
||||
load("@npm//@angular-devkit/architect-cli:index.bzl", "architect", "architect_test")
|
||||
@@ -73,7 +73,7 @@ APPLICATION_DEPS = link_packages([
|
||||
"@npm//@lezer/highlight",
|
||||
"@npm//@lezer/javascript",
|
||||
"@npm//@lezer/common",
|
||||
"@npm//xterm",
|
||||
"@npm//@xterm/xterm",
|
||||
"@npm//xterm-addon-fit",
|
||||
"@npm//angular-split",
|
||||
])
|
||||
|
||||
@@ -1,135 +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 {input, confirm} from '@inquirer/prompts';
|
||||
import {existsSync} from 'fs';
|
||||
import {copyFile, mkdir, writeFile} from 'fs/promises';
|
||||
import {dirname, join} from 'path';
|
||||
import {fileURLToPath} from 'url';
|
||||
import {copyFolder} from './utils/fs.mjs';
|
||||
|
||||
const EXAMPLES_BASE = join(
|
||||
dirname(fileURLToPath(import.meta.url)),
|
||||
'../../src/content/examples',
|
||||
);
|
||||
const STACKBLITZ_TEMPLATE = join(
|
||||
dirname(fileURLToPath(import.meta.url)),
|
||||
'../../src/content/stackblitz-template',
|
||||
);
|
||||
|
||||
const MAIN_TS_FILENAME = 'main.ts';
|
||||
const STACKBLITZ_CONFIG_FILENAME = 'stackblitz.json';
|
||||
|
||||
createExample();
|
||||
|
||||
async function createExample(): Promise<void> {
|
||||
console.info('Create example');
|
||||
const name = await input({message: 'What is the example name?'});
|
||||
const description = await input({message: 'Could you provide the description of the example?'});
|
||||
const tags: string[] = [];
|
||||
let addMoreTags = true;
|
||||
|
||||
while (addMoreTags) {
|
||||
const tag = await input({message: 'Could you provide the tag of the example?'});
|
||||
|
||||
if (!!tag) {
|
||||
tags.push(tag);
|
||||
}
|
||||
|
||||
console.info();
|
||||
|
||||
addMoreTags = await confirm({message: 'Would you like to add another tag?', default: true});
|
||||
}
|
||||
|
||||
const shouldCopySrcAppFolderFromTemplate = await confirm({
|
||||
message: 'Would you like to copy /src/app/**.* from template?',
|
||||
default: true,
|
||||
});
|
||||
const shouldCopyMainTsFromTemplate = await confirm({
|
||||
message: 'Would you like to copy /src/main.ts from template?',
|
||||
default: false,
|
||||
});
|
||||
|
||||
const examplePath = join(EXAMPLES_BASE, name);
|
||||
|
||||
await createEmptyExample(examplePath, name, description, tags);
|
||||
await copyFilesFromTemplate(
|
||||
examplePath,
|
||||
shouldCopySrcAppFolderFromTemplate,
|
||||
shouldCopyMainTsFromTemplate,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the directory and marker files for the new example.
|
||||
*/
|
||||
async function createEmptyExample(
|
||||
examplePath: string,
|
||||
exampleName: string,
|
||||
description: string,
|
||||
tags: string[],
|
||||
): Promise<void> {
|
||||
validateExampleName(exampleName);
|
||||
|
||||
await ensureExamplePath(examplePath);
|
||||
await writeStackBlitzFile(examplePath, description, tags);
|
||||
}
|
||||
|
||||
function validateExampleName(exampleName: string): void {
|
||||
if (/\s/.test(exampleName)) {
|
||||
throw new Error(`Unable to create example. The example name contains spaces: '${exampleName}'`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the new example directory exists.
|
||||
*/
|
||||
async function ensureExamplePath(examplePath: string): Promise<void> {
|
||||
if (existsSync(examplePath)) {
|
||||
throw new Error(
|
||||
`Unable to create example. The path to the new example already exists: ${examplePath}`,
|
||||
);
|
||||
}
|
||||
await mkdir(examplePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the `stackblitz.json` file into the new example.
|
||||
*/
|
||||
async function writeStackBlitzFile(
|
||||
examplePath: string,
|
||||
description: string,
|
||||
tags: string[],
|
||||
): Promise<void> {
|
||||
const config = {
|
||||
description,
|
||||
ignore: ['**/*.d.ts', '**/*.js', '**/*.[1,2].*'],
|
||||
tags,
|
||||
};
|
||||
await writeFile(
|
||||
join(examplePath, STACKBLITZ_CONFIG_FILENAME),
|
||||
JSON.stringify(config, null, 2) + '\n',
|
||||
);
|
||||
}
|
||||
|
||||
async function copyFilesFromTemplate(
|
||||
examplePath: string,
|
||||
shouldCopySrcAppFolderFromTemplate: boolean,
|
||||
shouldCopyMainTsFromTemplate: boolean,
|
||||
): Promise<void> {
|
||||
if (shouldCopySrcAppFolderFromTemplate) {
|
||||
const appTemplateFolderPath = join(STACKBLITZ_TEMPLATE, 'src/app');
|
||||
const exampleAppFolderPath = join(examplePath, 'src/app');
|
||||
await copyFolder(appTemplateFolderPath, exampleAppFolderPath);
|
||||
}
|
||||
if (shouldCopyMainTsFromTemplate) {
|
||||
const mainTsTemplatePath = join(STACKBLITZ_TEMPLATE, 'src', MAIN_TS_FILENAME);
|
||||
const exampleMainTsPath = join(examplePath, 'src', MAIN_TS_FILENAME);
|
||||
await copyFile(mainTsTemplatePath, exampleMainTsPath);
|
||||
}
|
||||
}
|
||||
@@ -1,158 +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 ts from 'typescript';
|
||||
import * as fs from 'fs';
|
||||
import {dirname, join, relative} from 'path';
|
||||
import {fileURLToPath} from 'url';
|
||||
import {readFile, writeFile} from 'fs/promises';
|
||||
|
||||
const TYPESCRIPT_EXTENSION = '.ts';
|
||||
const SKIP_FILES_WITH_EXTENSIONS = ['.e2e-spec.ts', '.spec.ts', '.po.ts'];
|
||||
const EXAMPLES_PATH = `../../content/examples`;
|
||||
|
||||
interface File {
|
||||
path: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
interface AnalyzedFiles {
|
||||
path: string;
|
||||
componentNames: string[];
|
||||
}
|
||||
|
||||
/** Absolute disk path to the project directory. */
|
||||
const examplesDir = join(
|
||||
dirname(fileURLToPath(import.meta.url)),
|
||||
'../../src/content/examples',
|
||||
);
|
||||
const projectDir = join(dirname(fileURLToPath(import.meta.url)), '../../src');
|
||||
const previewsComponentMapFileName = 'previews.ts';
|
||||
|
||||
main();
|
||||
|
||||
async function main() {
|
||||
const files = await retrieveAllTypescriptFiles(
|
||||
examplesDir,
|
||||
(path) => !SKIP_FILES_WITH_EXTENSIONS.some((extensionToSkip) => path.endsWith(extensionToSkip)),
|
||||
);
|
||||
|
||||
const filesWithComponent = files
|
||||
.map((file) => ({
|
||||
componentNames: analyzeFile(file),
|
||||
path: file.path,
|
||||
}))
|
||||
.filter((result) => result.componentNames.length > 0);
|
||||
|
||||
const previewsComponentMap = generatePreviewsComponentMap(filesWithComponent);
|
||||
|
||||
fs.mkdirSync(join(projectDir, 'assets/previews'), {recursive: true});
|
||||
|
||||
await writeFile(
|
||||
join(projectDir, 'assets/previews', previewsComponentMapFileName),
|
||||
previewsComponentMap,
|
||||
);
|
||||
}
|
||||
|
||||
/** Recursively search the provided directory for all typescript files and asynchronously load them. */
|
||||
function retrieveAllTypescriptFiles(
|
||||
baseDir: string,
|
||||
predicateFn: (path: string) => boolean,
|
||||
): Promise<File[]> {
|
||||
const typescriptFiles: Promise<File>[] = [];
|
||||
|
||||
const checkFilesInDirectory = (dir: string) => {
|
||||
const files = fs.readdirSync(dir, {withFileTypes: true});
|
||||
for (const file of files) {
|
||||
const fullPathToFile = join(dir, file.name);
|
||||
const relativeFilePath = relative(baseDir, fullPathToFile);
|
||||
|
||||
if (
|
||||
file.isFile() &&
|
||||
file.name.endsWith(TYPESCRIPT_EXTENSION) &&
|
||||
predicateFn(relativeFilePath)
|
||||
) {
|
||||
typescriptFiles.push(
|
||||
readFile(fullPathToFile, {encoding: 'utf-8'}).then((fileContent) => {
|
||||
return {
|
||||
path: relativeFilePath,
|
||||
content: fileContent,
|
||||
};
|
||||
}),
|
||||
);
|
||||
} else if (file.isDirectory()) {
|
||||
checkFilesInDirectory(fullPathToFile);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
checkFilesInDirectory(baseDir);
|
||||
|
||||
return Promise.all(typescriptFiles);
|
||||
}
|
||||
|
||||
/** Returns list of the `Standalone` @Component class names for given file */
|
||||
function analyzeFile(file: File): string[] {
|
||||
const componentClassNames: string[] = [];
|
||||
const sourceFile = ts.createSourceFile(file.path, file.content, ts.ScriptTarget.Latest, false);
|
||||
|
||||
const visitNode = (node: ts.Node): void => {
|
||||
if (ts.isClassDeclaration(node)) {
|
||||
const decorators = ts.getDecorators(node);
|
||||
const componentName = node.name ? node.name.text : null;
|
||||
|
||||
if (decorators && decorators.length) {
|
||||
for (const decorator of decorators) {
|
||||
const call = decorator.expression;
|
||||
|
||||
if (
|
||||
ts.isCallExpression(call) &&
|
||||
ts.isIdentifier(call.expression) &&
|
||||
call.expression.text === 'Component' &&
|
||||
call.arguments.length > 0 &&
|
||||
ts.isObjectLiteralExpression(call.arguments[0])
|
||||
) {
|
||||
const hasStandaloneProperty = call.arguments[0].properties.some(
|
||||
(property) =>
|
||||
property.name &&
|
||||
ts.isIdentifier(property.name) &&
|
||||
property.name.text === 'standalone',
|
||||
);
|
||||
|
||||
if (hasStandaloneProperty && componentName) {
|
||||
componentClassNames.push(componentName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ts.forEachChild(node, visitNode);
|
||||
};
|
||||
|
||||
visitNode(sourceFile);
|
||||
|
||||
return componentClassNames;
|
||||
}
|
||||
|
||||
function generatePreviewsComponentMap(data: AnalyzedFiles[]): string {
|
||||
let result = '';
|
||||
for (const fileData of data) {
|
||||
for (const componentName of fileData.componentNames) {
|
||||
const key = `${fileData.path}${
|
||||
fileData.componentNames.length > 1 ? '_' + componentName : ''
|
||||
}`.replace(/\\/g, '/');
|
||||
result += `['${key}']: () => import('${EXAMPLES_PATH}/${fileData.path
|
||||
.replace(/\\/g, '/')
|
||||
.replace('.ts', '')}').then(c => c.${componentName}),\n`;
|
||||
}
|
||||
}
|
||||
return fs
|
||||
.readFileSync('scripts/examples/previews-components.template', 'utf8')
|
||||
.replace(/\${previewsComponents}/g, result);
|
||||
}
|
||||
@@ -1,23 +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
|
||||
*/
|
||||
|
||||
/**
|
||||
******************************************************************************
|
||||
* DO NOT MANUALLY EDIT THIS FILE. THIS FILE IS AUTOMATICALLY GENERATED.
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
import {Type} from '@angular/core';
|
||||
|
||||
/**
|
||||
* Map of the previews components, values are functions which returns the promise of the component type, which will be displayed as preview in the ExampleViewer component.
|
||||
* Keys has to be equal to paths written down in the docs markdown files.
|
||||
*/
|
||||
export const PREVIEWS_COMPONENTS_MAP: Record<string, () => Promise<Type<unknown>>> = {
|
||||
${previewsComponents}
|
||||
};
|
||||
@@ -1,221 +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 {dirname, join} from 'path';
|
||||
import {readFile, writeFile} from 'fs/promises';
|
||||
import {
|
||||
ASSETS_EXAMPLE_PATH,
|
||||
CSS_TS_COPYRIGHT,
|
||||
EXAMPLES_PATH,
|
||||
HTML_COPYRIGHT,
|
||||
STACKBLITZ_TEMPLATE_PATH,
|
||||
STACKBLITZ_CONFIG_FILENAME,
|
||||
TEMPORARY_EXAMPLES_PATH,
|
||||
EXCLUDE_FILES_FOR_STACKBLITZ,
|
||||
} from './utils/examples-constants.mjs';
|
||||
import {copyFolder, createFolder, removeFolder} from './utils/fs.mjs';
|
||||
import jsdom from 'jsdom';
|
||||
import {glob} from 'glob';
|
||||
import {regionParser} from './../../prerender/markdown-pipeline/regions/region-parser.mjs';
|
||||
|
||||
interface StackblitzConfig {
|
||||
ignore: string[];
|
||||
file: string;
|
||||
tags: string[];
|
||||
description: string;
|
||||
}
|
||||
|
||||
export async function generateStackblitzExample(
|
||||
exampleFolderName: string,
|
||||
primaryFilePath: string,
|
||||
title: string,
|
||||
) {
|
||||
const exampleDir = join(EXAMPLES_PATH, exampleFolderName);
|
||||
const temporaryExampleDir = join(TEMPORARY_EXAMPLES_PATH, exampleFolderName);
|
||||
const config = await readFile(join(exampleDir, STACKBLITZ_CONFIG_FILENAME), 'utf-8');
|
||||
const stackblitzConfig: StackblitzConfig = JSON.parse(config);
|
||||
primaryFilePath = join(...primaryFilePath.split('/'));
|
||||
|
||||
await createFolder(temporaryExampleDir);
|
||||
await combineTemplateWithExample(exampleDir, temporaryExampleDir);
|
||||
await generateStackblitzHtml(
|
||||
temporaryExampleDir,
|
||||
stackblitzConfig,
|
||||
exampleFolderName,
|
||||
primaryFilePath,
|
||||
title,
|
||||
);
|
||||
await removeFolder(temporaryExampleDir);
|
||||
}
|
||||
|
||||
async function combineTemplateWithExample(
|
||||
exampleDir: string,
|
||||
temporaryExampleDir: string,
|
||||
): Promise<void> {
|
||||
// Copy template files to TEMP folder
|
||||
await copyFolder(STACKBLITZ_TEMPLATE_PATH, temporaryExampleDir);
|
||||
|
||||
// Copy example files to TEMP folder
|
||||
await copyFolder(exampleDir, temporaryExampleDir);
|
||||
}
|
||||
|
||||
async function generateStackblitzHtml(
|
||||
temporaryExampleDir: string,
|
||||
stackBlitzConfig: StackblitzConfig,
|
||||
exampleFolderName: string,
|
||||
primaryFilePath: string,
|
||||
title: string,
|
||||
): Promise<void> {
|
||||
const defaultIncludes = [
|
||||
'**/*.ts',
|
||||
'**/*.js',
|
||||
'**/*.css',
|
||||
'**/*.html',
|
||||
'**/*.md',
|
||||
'**/*.json',
|
||||
'**/*.svg',
|
||||
];
|
||||
const exampleFilePaths = await glob(defaultIncludes, {
|
||||
cwd: temporaryExampleDir,
|
||||
nodir: true,
|
||||
dot: true,
|
||||
ignore: stackBlitzConfig.ignore,
|
||||
});
|
||||
|
||||
const postData = await createPostData(
|
||||
temporaryExampleDir,
|
||||
stackBlitzConfig,
|
||||
exampleFilePaths,
|
||||
title,
|
||||
);
|
||||
const primaryFile = getPrimaryFile(primaryFilePath ?? stackBlitzConfig.file, exampleFilePaths);
|
||||
const html = createStackblitzHtml(postData, primaryFile);
|
||||
|
||||
const stackblitzHtmlPath = join(
|
||||
join(ASSETS_EXAMPLE_PATH, exampleFolderName),
|
||||
`${primaryFile}.html`,
|
||||
);
|
||||
await createFolder(dirname(stackblitzHtmlPath));
|
||||
await writeFile(stackblitzHtmlPath, html, 'utf-8');
|
||||
}
|
||||
|
||||
function getPrimaryFile(primaryFilePath: string, exampleFilePaths: string[]): string {
|
||||
if (primaryFilePath) {
|
||||
if (!exampleFilePaths.some((filePath) => filePath === primaryFilePath)) {
|
||||
throw new Error(`The specified primary file (${primaryFilePath}) does not exist!`);
|
||||
}
|
||||
return primaryFilePath;
|
||||
} else {
|
||||
const defaultPrimaryFilePaths = [
|
||||
'src/app/app.component.html',
|
||||
'src/app/app.component.ts',
|
||||
'src/app/main.ts',
|
||||
];
|
||||
const primaryFile = defaultPrimaryFilePaths.find((path) =>
|
||||
exampleFilePaths.some((filePath) => filePath === path),
|
||||
);
|
||||
|
||||
if (!primaryFile) {
|
||||
throw new Error(
|
||||
`None of the default primary files (${defaultPrimaryFilePaths.join(', ')}) exists.`,
|
||||
);
|
||||
}
|
||||
|
||||
return primaryFile;
|
||||
}
|
||||
}
|
||||
|
||||
async function createPostData(
|
||||
exampleDir: string,
|
||||
config: StackblitzConfig,
|
||||
exampleFilePaths: string[],
|
||||
title: string,
|
||||
): Promise<Record<string, string>> {
|
||||
const postData: Record<string, string> = {};
|
||||
|
||||
for (const filePath of exampleFilePaths) {
|
||||
if (EXCLUDE_FILES_FOR_STACKBLITZ.some((excludedFile) => filePath.endsWith(excludedFile))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let content = await readFile(join(exampleDir, filePath), 'utf-8');
|
||||
content = appendCopyright(filePath, content);
|
||||
content = extractRegions(filePath, content);
|
||||
|
||||
postData[`project[files][${filePath}]`] = content;
|
||||
}
|
||||
|
||||
const tags = ['angular', 'example', ...(config.tags || [])];
|
||||
tags.forEach((tag, index) => (postData[`project[tags][${index}]`] = tag));
|
||||
|
||||
postData['project[description]'] = `Angular Example - ${config.description}`;
|
||||
postData['project[template]'] = 'node';
|
||||
postData['project[title]'] = title ?? 'Angular Example';
|
||||
|
||||
return postData;
|
||||
}
|
||||
|
||||
function createStackblitzHtml(postData: Record<string, string>, primaryFile: string): string {
|
||||
const baseHtml = createBaseStackblitzHtml(primaryFile);
|
||||
const doc = new jsdom.JSDOM(baseHtml).window.document;
|
||||
const form = doc.querySelector('form');
|
||||
|
||||
for (const [key, value] of Object.entries(postData)) {
|
||||
const element = htmlToElement(doc, `<input type="hidden" name="${key}">`);
|
||||
if (element && form) {
|
||||
element.setAttribute('value', value as string);
|
||||
form.appendChild(element);
|
||||
}
|
||||
}
|
||||
|
||||
return doc.documentElement.outerHTML;
|
||||
}
|
||||
|
||||
function createBaseStackblitzHtml(primaryFile: string) {
|
||||
const file = `?file=${primaryFile}`;
|
||||
const action = `https://stackblitz.com/run${file}`;
|
||||
|
||||
return `
|
||||
<!DOCTYPE html><html lang="en"><body>
|
||||
<form id="mainForm" method="post" action="${action}" target="_self"></form>
|
||||
<script>
|
||||
var embedded = 'ctl=1';
|
||||
var isEmbedded = window.location.search.indexOf(embedded) > -1;
|
||||
|
||||
if (isEmbedded) {
|
||||
var form = document.getElementById('mainForm');
|
||||
var action = form.action;
|
||||
var actionHasParams = action.indexOf('?') > -1;
|
||||
var symbol = actionHasParams ? '&' : '?'
|
||||
form.action = form.action + symbol + embedded;
|
||||
}
|
||||
document.getElementById("mainForm").submit();
|
||||
</script>
|
||||
</body></html>
|
||||
`.trim();
|
||||
}
|
||||
|
||||
function appendCopyright(filename: string, content: string): string {
|
||||
if (filename.endsWith('.html')) {
|
||||
return `${HTML_COPYRIGHT}${content}`;
|
||||
} else if (filename.endsWith('.ts') || filename.endsWith('.css')) {
|
||||
return `${CSS_TS_COPYRIGHT}${content}`;
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
function htmlToElement(document: Document, html: string) {
|
||||
const div = document.createElement('div');
|
||||
div.innerHTML = html;
|
||||
return div.firstElementChild;
|
||||
}
|
||||
|
||||
function extractRegions(path: string, contents: string): string {
|
||||
const regionParserResult = regionParser(contents, path);
|
||||
return regionParserResult.contents;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"ts-node": {
|
||||
"compilerOptions": {
|
||||
"module": "es2020"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,55 +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 {dirname, join} from 'path';
|
||||
import {fileURLToPath} from 'url';
|
||||
|
||||
const BASE = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// Directory paths
|
||||
export const ASSETS_EXAMPLE_PATH = join(
|
||||
BASE,
|
||||
'../../../src/assets/content/examples',
|
||||
);
|
||||
export const EXAMPLES_PATH = join(BASE, '../../../src/content/examples');
|
||||
export const STACKBLITZ_TEMPLATE_PATH = join(
|
||||
BASE,
|
||||
'../../../src/content/stackblitz-template',
|
||||
);
|
||||
|
||||
export const TEMPORARY_EXAMPLES_PATH = join(fileURLToPath(import.meta.url), '../../../../TEMP');
|
||||
|
||||
// Extensions
|
||||
export const TEST_FILES_EXTENSION_SUFFIX = '.spec.ts';
|
||||
export const TEST_FILES_E2E_EXTENSION_SUFFIX = '.e2e-spec.ts';
|
||||
|
||||
// Filename
|
||||
// TODO: BUILD.bazel and example-config will be deprecated when migration to standalone examples will be done.
|
||||
export const BUILD_BAZEL_FILENAME = 'BUILD.bazel';
|
||||
export const EXAMPLE_CONFIG_FILENAME = 'example-config.json';
|
||||
export const STACKBLITZ_CONFIG_FILENAME = 'stackblitz.json';
|
||||
|
||||
// Copyright
|
||||
const PAD = '\n\n';
|
||||
const COPYRIGHT =
|
||||
'@license\n' +
|
||||
'Copyright Google LLC All Rights Reserved.\n' +
|
||||
'\n' +
|
||||
'Use of this source code is governed by an MIT-style license that can be\n' +
|
||||
'found in the LICENSE file at https://angular.dev/license\n';
|
||||
export const CSS_TS_COPYRIGHT = `/*\n${COPYRIGHT}\n*/${PAD}`;
|
||||
export const HTML_COPYRIGHT = `<!-- \n${COPYRIGHT}\n-->${PAD}`;
|
||||
|
||||
// Exclude
|
||||
export const EXCLUDE_FILES_FOR_STACKBLITZ = [
|
||||
STACKBLITZ_CONFIG_FILENAME,
|
||||
BUILD_BAZEL_FILENAME,
|
||||
EXAMPLE_CONFIG_FILENAME,
|
||||
TEST_FILES_EXTENSION_SUFFIX,
|
||||
TEST_FILES_E2E_EXTENSION_SUFFIX,
|
||||
];
|
||||
@@ -1,77 +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 {Dirent, existsSync, readdirSync} from 'fs';
|
||||
import {copyFile, mkdir, readFile, readdir, rm, stat} from 'fs/promises';
|
||||
import {join, relative} from 'path';
|
||||
|
||||
export async function copyFolder(source: string, destination: string) {
|
||||
if (!existsSync(destination)) {
|
||||
await mkdir(destination, {recursive: true});
|
||||
}
|
||||
|
||||
const files = await readdir(source);
|
||||
|
||||
for (const file of files) {
|
||||
const sourcePath = join(source, file);
|
||||
const destPath = join(destination, file);
|
||||
|
||||
const stats = await stat(sourcePath);
|
||||
const isDirectory = await stats.isDirectory();
|
||||
|
||||
if (isDirectory) {
|
||||
await copyFolder(sourcePath, destPath);
|
||||
} else {
|
||||
await copyFile(sourcePath, destPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Recursively search the provided directory for all files which satisfy predicate and asynchronously load them. */
|
||||
export function retrieveFiles(
|
||||
baseDir: string,
|
||||
predicate?: (file: Dirent) => boolean,
|
||||
): Promise<{path: string; content: string}[]> {
|
||||
const filesWithContent: Promise<{path: string; content: string}>[] = [];
|
||||
|
||||
const checkFilesInDirectory = (dir: string) => {
|
||||
const files = readdirSync(dir, {withFileTypes: true});
|
||||
for (const file of files) {
|
||||
const fullPathToFile = join(dir, file.name);
|
||||
|
||||
if (file.isFile() && (predicate ? predicate(file) : true)) {
|
||||
filesWithContent.push(
|
||||
readFile(fullPathToFile, {encoding: 'utf-8'}).then((fileContent) => {
|
||||
return {
|
||||
path: relative(baseDir, fullPathToFile),
|
||||
content: fileContent,
|
||||
};
|
||||
}),
|
||||
);
|
||||
} else if (file.isDirectory()) {
|
||||
checkFilesInDirectory(fullPathToFile);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
checkFilesInDirectory(baseDir);
|
||||
|
||||
return Promise.all(filesWithContent);
|
||||
}
|
||||
|
||||
export async function createFolder(path: string): Promise<void> {
|
||||
if (!existsSync(path)) {
|
||||
await mkdir(path, {recursive: true});
|
||||
}
|
||||
}
|
||||
|
||||
export async function removeFolder(path: string): Promise<void> {
|
||||
if (existsSync(path)) {
|
||||
await rm(path, {recursive: true});
|
||||
}
|
||||
}
|
||||
@@ -116,7 +116,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<p class="docs-license">
|
||||
Super-powered by Google ©2010-2023. Code licensed under an
|
||||
Super-powered by Google ©2010-2024. Code licensed under an
|
||||
<a routerLink="/license" title="License text">MIT-style License</a>
|
||||
. Documentation licensed under
|
||||
<a href="https://creativecommons.org/licenses/by/4.0/">CC BY 4.0</a>
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
# EmbeddedEditor components, services and functionality
|
||||
|
||||
- [Scenarios](#scenarios)
|
||||
- [Loading a project](#loading-a-project)
|
||||
- [Updating the code](#updating-the-code)
|
||||
- [Creating a new file](#creating-a-new-file)
|
||||
- [Deleting a file](#deleting-a-file)
|
||||
- [Switching a project](#switching-a-project)
|
||||
- [Components and services](#components-and-services)
|
||||
|
||||
- [EmbeddedEditor](#EmbeddedEditor)
|
||||
- [CodeEditor](#CodeEditor)
|
||||
- [CodeMirrorEditor](#CodeMirrorEditor)
|
||||
- [TypeScript Web Worker](#typescript-web-worker)
|
||||
- [Preview](#Preview)
|
||||
- [Terminal](#Terminal)
|
||||
- [InteractiveTerminal](#InteractiveTerminal)
|
||||
- [Console](#Console)
|
||||
- [NodeRuntimeSandbox](#NodeRuntimeSandbox)
|
||||
- [NodeRuntimeState](#NodeRuntimeState)
|
||||
- [EmbeddedTutorialManager](#EmbeddedTutorialManager)
|
||||
- [EditorUiState](#EditorUiState)
|
||||
- [DownloadManager](#DownloadManager)
|
||||
- [AlertManager](#AlertManager)
|
||||
- [TypingsLoader](#TypingsLoader)
|
||||
|
||||
## External libraries
|
||||
|
||||
- [WebContainers API](https://webcontainers.io/)
|
||||
- [CodeMirror](https://codemirror.net/)
|
||||
- [@typescript/vfs](https://www.npmjs.com/package/@typescript/vfs)
|
||||
- [Xterm.js](https://xtermjs.org/)
|
||||
|
||||
## Notes
|
||||
|
||||
- See [scripts/tutorials/README.md](/scripts/tutorials/README.md) for more information about the tutorials script.
|
||||
- See [adev/src/content/tutorials/README.md](/adev/src/content/tutorials/README.md) for more information about the tutorials content.
|
||||
|
||||
---
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Loading a project
|
||||
|
||||
1. The page responsible for the embedded editor lazy loads the [`EmbeddedEditor`](./embedded-editor.component.ts) component and the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts), then triggers the initialization of all components and services. The embedded editor is available in the following pages:
|
||||
|
||||
- homepage: https://angular.dev
|
||||
- playground: https://angular.dev/playground
|
||||
- tutorial pages: https://angular.dev/tutorials
|
||||
|
||||
2. The project assets are fetched by the [`EmbeddedTutorialManager`](./embedded-tutorial-manager.service.ts). Meanwhile:
|
||||
|
||||
- The code editor is initialized
|
||||
- The code editor initializes the TypeScript Web Worker, which initializes the "default file system map" using TypeScript's CDN.
|
||||
- The WebContainer is initialized
|
||||
- The terminal is initialized
|
||||
|
||||
3. The tutorial source code is mounted in the `WebContainer`'s filesystem by the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts)
|
||||
4. The tutorial project dependencies are installed by the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts).
|
||||
5. The development server is started by the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts) and the types are loaded by the [`TypingsLoader`](./typings-loader.service.ts) service.
|
||||
6. The preview is loaded with the URL provided by the WebContainer API after the development server is started.
|
||||
7. The project is ready.
|
||||
|
||||
### Updating the code
|
||||
|
||||
1. The user update the code in the code editor.
|
||||
2. The code editor state is updated on real time, without debouncing so that the user can see the changes in the code editor and CodeMirror can handle the changes accordingly.
|
||||
3. At the same time, the changes are sent to the TypeScript web worker to provide diagnostics, autocomplete and type features as soon as possible.
|
||||
4. The code changes are debounced to be written in the WebContainer filesystem by the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts).
|
||||
5. After the debounce time is reached, the code changes are written in the WebContainer filesystem by the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts), then the user can see the changes in the preview.
|
||||
|
||||
### Creating a new file
|
||||
|
||||
1. The user clicks on the new file button.
|
||||
2. The new file tab is opened.
|
||||
3. The user types the new file name.
|
||||
4. If the file name is valid, the file is created in the WebContainer filesystem by the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts).
|
||||
|
||||
- `..` is disallowed in the file name to prevent users to create files outside the `src` directory.
|
||||
|
||||
5. The file is added to the TypeScript virtual file system, allowing the TypeScript web worker to provide diagnostics, autocomplete and type features for the new file. Also, exports from the new file are available in other files.
|
||||
6. The new file is added as the last tab in the code editor and the new file can be edited.
|
||||
|
||||
Note: If the new file name matches a file that already exists but is hidden in the code editor, the content for that file will show up in the created file. An example for a file that always exists is `index.html`.
|
||||
|
||||
### Deleting a file
|
||||
|
||||
1. The user clicks on the delete file button.
|
||||
2. The file is deleted from the WebContainer filesystem by the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts).
|
||||
3. The file is removed from the TypeScript virtual file system.
|
||||
4. The file is removed from the code editor tabs.
|
||||
|
||||
Note: Some files can't be deleted to prevent users to break the app, being `src/main.ts`and `src/index.html`
|
||||
|
||||
### Switching a project
|
||||
|
||||
The embedded editor considers a project change when the embedded editor was already initialized and the user changes the page in the following scenarios:
|
||||
|
||||
- Navigating through tutorial steps
|
||||
- Going from the homepage after the embedded editor is initialized to the playground
|
||||
- Going from a tutorial page to the playground
|
||||
- Going from a tutorial page to the homepage
|
||||
- Going from the playground to the homepage
|
||||
|
||||
When a project change is detected, the [`EmbeddedTutorialManager`](./embedded-tutorial-manager.service.ts) emits the `tutorialChanged` observable, which is listened in multiple sub-components and services, then each component/service performs the necessary operations to switch the project.
|
||||
|
||||
The following steps are executed on project change:
|
||||
|
||||
1. The new project files are fetched by the [`EmbeddedTutorialManager`](./embedded-tutorial-manager.service.ts).
|
||||
2. The new project files are mounted in the WebContainer filesystem.
|
||||
3. The TypeScript virtual filesystem is updated with the new files and contents.
|
||||
4. The previous project and new project files are compared.
|
||||
1. Files that are not available in the new project are deleted from the WebContainer filesystem.
|
||||
2. Files that have the same path and name have their content replaced on the previous step when the files are mounted.
|
||||
5. The previous project dependencies are compared with the new project dependencies.
|
||||
1. If there are differences, a `npm install` is triggered, hiding the preview and going to the install loading step.
|
||||
2. If there are no differences, the project is ready.
|
||||
6. Some states are resetted, for example the "reveal answer" state if the previous project was in the "reveal answer" state.
|
||||
|
||||
## Components and services
|
||||
|
||||
### [`EmbeddedEditor`](./embedded-editor.component.ts)
|
||||
|
||||
The embedded editor is the parent component that holds all the components and services that compose the embedded editor.
|
||||
|
||||
#### [`CodeEditor`](./code-editor/code-editor.component.ts)
|
||||
|
||||
The component that holds the code editor view and the code editor state.
|
||||
|
||||
##### [`CodeMirrorEditor`](./code-editor/code-mirror-editor.service.ts)
|
||||
|
||||
[CodeMirror](https://codemirror.net/) is the library used to handle the code editor.
|
||||
|
||||
The `CodeMirrorEditor` service manages the CodeMirror instance and all the interactions with the library used to handle the code editor.
|
||||
|
||||
- handle the file edits and the CodeMirror view and state
|
||||
- handle the current project files in the code editor
|
||||
- handle the file creations and deletions
|
||||
- handle the file changes
|
||||
- handle all the CodeMirror specific events and extensions
|
||||
|
||||
###### [TypeScript Web Worker](./code-editor/workers/typescript-vfs.worker.ts)
|
||||
|
||||
The TypeScript features are provided by the TypeScript web worker, that is initialized by the `CodeMirrorEditor` service.
|
||||
|
||||
The TypeScript web worker uses `@typescript/vfs` and the TypeScript language service to provide diagnostics, autocomplete and type features.
|
||||
|
||||
#### [`Preview`](./preview/preview.component.ts)
|
||||
|
||||
The preview component manages the `iframe` responsible for displaying the tutorial project preview, with the URL provided by the WebContainer API after the development server is started.
|
||||
|
||||
While the project is being initialized, the preview displays the loading state.
|
||||
|
||||
#### [`Terminal`](./terminal/terminal.component.ts)
|
||||
|
||||
[Xterm.js](https://xtermjs.org/) is the library used to handle the terminals.
|
||||
|
||||
The terminal component handles the Xterm.js instance for the console and for the interactive terminal.
|
||||
|
||||
##### [`InteractiveTerminal`](./terminal/interactive-terminal.ts)
|
||||
|
||||
The interactive terminal is the terminal where the user can interact with the terminal and run commands, supporting only commands for the Angular CLI.
|
||||
|
||||
##### Console
|
||||
|
||||
The console displays the output for `npm install` and `ng serve`.
|
||||
|
||||
#### [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts)
|
||||
|
||||
Responsible for managing the WebContainer instance and all communication with its API. This service handles:
|
||||
|
||||
- the WebContainer instance
|
||||
- all Node.js scripts
|
||||
- the WebContainer filesystem, mounting the tutorial project files, writing new content, deleting and creating files.
|
||||
- the terminal session, reading and processing user inputs.
|
||||
- the tutorial project dependencies, installing the dependencies.
|
||||
- the processes running inside the WebContainer, being the npm scripts to install the dependencies, run the development server and the user inputs for the `ng` CLI.
|
||||
|
||||
##### [`NodeRuntimeState`](./node-runtime-state.service.ts)
|
||||
|
||||
Manages the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts) loading and error state.
|
||||
|
||||
#### [`EmbeddedTutorialManager`](./embedded-tutorial-manager.service.ts)
|
||||
|
||||
Manages the tutorial assets, being responsible for fetching the tutorial source code and metadata.
|
||||
|
||||
The source code is mounted in the WebContainer filesystem by the [`NodeRuntimeSandbox`](./node-runtime-sandbox.service.ts).
|
||||
|
||||
The metadata is used to manage the project, handle the project changes and the user interactivity with the app.
|
||||
|
||||
This service also handles the reveal answer and reset reveal answer feature.
|
||||
|
||||
#### [`EditorUiState`](./editor-ui-state.service.ts)
|
||||
|
||||
Manages the editor UI state, being responsible for handling the user interactions with the editor tabs, switching between the preview, the terminal and the console.
|
||||
|
||||
#### [`DownloadManager`](./download-manager.service.ts)
|
||||
|
||||
Responsible for handling the download button in the embedded editor, fetching the tutorial project files and generating a zip file with the project content.
|
||||
|
||||
#### [`AlertManager`](./alert-manager.service.ts)
|
||||
|
||||
Manage the alerts displayed in the embedded editor, being the out of memory alert when multiple tabs are opened, and unsupported enviroments alerts.
|
||||
|
||||
#### [`TypingsLoader`](./typings-loader.service.ts)
|
||||
|
||||
Manages the types definitions for the code editor.
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import {inject} from '@angular/core';
|
||||
import {Subject} from 'rxjs';
|
||||
import {Terminal} from 'xterm';
|
||||
import {Terminal} from '@xterm/xterm';
|
||||
|
||||
import {WINDOW} from '@angular/docs';
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Terminal} from 'xterm';
|
||||
import {Terminal} from '@xterm/xterm';
|
||||
import {FitAddon} from 'xterm-addon-fit';
|
||||
import {InteractiveTerminal} from './interactive-terminal';
|
||||
|
||||
|
||||
@@ -157,6 +157,11 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
|
||||
path: 'guide/components/advanced-configuration',
|
||||
contentPath: 'guide/components/advanced-configuration',
|
||||
},
|
||||
{
|
||||
label: 'Custom Elements',
|
||||
path: 'guide/elements',
|
||||
contentPath: 'guide/elements',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -218,9 +223,39 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
|
||||
contentPath: 'guide/templates/control-flow',
|
||||
},
|
||||
{
|
||||
label: 'Deferrable views',
|
||||
path: 'guide/defer',
|
||||
contentPath: 'guide/defer',
|
||||
label: 'Pipes',
|
||||
children: [
|
||||
{
|
||||
label: 'Overview',
|
||||
path: 'guide/pipes',
|
||||
contentPath: 'guide/pipes/overview',
|
||||
},
|
||||
{
|
||||
label: 'Using a pipe in a template',
|
||||
path: 'guide/pipes/template',
|
||||
contentPath: 'guide/pipes/template',
|
||||
},
|
||||
{
|
||||
label: 'Custom pipes',
|
||||
path: 'guide/pipes/transform-data',
|
||||
contentPath: 'guide/pipes/transform-data',
|
||||
},
|
||||
{
|
||||
label: 'Pipe precedence in expressions',
|
||||
path: 'guide/pipes/precedence',
|
||||
contentPath: 'guide/pipes/precedence',
|
||||
},
|
||||
{
|
||||
label: 'Change detection with pipes',
|
||||
path: 'guide/pipes/change-detection',
|
||||
contentPath: 'guide/pipes/change-detection',
|
||||
},
|
||||
{
|
||||
label: 'Unwrapping data from an observable',
|
||||
path: 'guide/pipes/unwrapping-data-observables',
|
||||
contentPath: 'guide/pipes/unwrapping-data-observables',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Template reference variables',
|
||||
@@ -317,6 +352,16 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
|
||||
path: 'guide/signals/rxjs-interop',
|
||||
contentPath: 'guide/signals/rxjs-interop',
|
||||
},
|
||||
{
|
||||
label: 'Inputs as signals',
|
||||
path: 'guide/signals/inputs',
|
||||
contentPath: 'guide/signals/inputs',
|
||||
},
|
||||
{
|
||||
label: 'Model inputs',
|
||||
path: 'guide/signals/model',
|
||||
contentPath: 'guide/signals/model',
|
||||
},
|
||||
{
|
||||
label: 'Queries as signals',
|
||||
path: 'guide/signals/queries',
|
||||
@@ -324,71 +369,6 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'NgModule',
|
||||
children: [
|
||||
{
|
||||
label: 'Overview',
|
||||
path: 'guide/ngmodules',
|
||||
contentPath: 'guide/ngmodules/overview',
|
||||
},
|
||||
{
|
||||
label: 'JS Modules vs NgModules',
|
||||
path: 'guide/ngmodules/vs-jsmodule',
|
||||
contentPath: 'guide/ngmodules/vs-jsmodule',
|
||||
},
|
||||
{
|
||||
label: 'Launching your app with a root module',
|
||||
path: 'guide/ngmodules/bootstrapping',
|
||||
contentPath: 'guide/ngmodules/bootstrapping',
|
||||
},
|
||||
{
|
||||
label: 'Sharing NgModules',
|
||||
path: 'guide/ngmodules/sharing',
|
||||
contentPath: 'guide/ngmodules/sharing',
|
||||
},
|
||||
{
|
||||
label: 'Frequently used NgModules',
|
||||
path: 'guide/ngmodules/frequent',
|
||||
contentPath: 'guide/ngmodules/frequent',
|
||||
},
|
||||
{
|
||||
label: 'Feature modules',
|
||||
path: 'guide/ngmodules/feature-modules',
|
||||
contentPath: 'guide/ngmodules/feature-modules',
|
||||
},
|
||||
{
|
||||
label: 'Types of feature modules',
|
||||
path: 'guide/ngmodules/module-types',
|
||||
contentPath: 'guide/ngmodules/module-types',
|
||||
},
|
||||
{
|
||||
label: 'Providing dependencies',
|
||||
path: 'guide/ngmodules/providers',
|
||||
contentPath: 'guide/ngmodules/providers',
|
||||
},
|
||||
{
|
||||
label: 'Singleton services',
|
||||
path: 'guide/ngmodules/singleton-services',
|
||||
contentPath: 'guide/ngmodules/singleton-services',
|
||||
},
|
||||
{
|
||||
label: 'Lazy-loading feature modules',
|
||||
path: 'guide/ngmodules/lazy-loading',
|
||||
contentPath: 'guide/ngmodules/lazy-loading',
|
||||
},
|
||||
{
|
||||
label: 'NgModule API',
|
||||
path: 'guide/ngmodules/api',
|
||||
contentPath: 'guide/ngmodules/api',
|
||||
},
|
||||
{
|
||||
label: 'NgModule FAQs',
|
||||
path: 'guide/ngmodules/faq',
|
||||
contentPath: 'guide/ngmodules/faq',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Routing',
|
||||
children: [
|
||||
@@ -455,29 +435,64 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Server-side Rendering',
|
||||
path: 'guide/ssr',
|
||||
contentPath: 'guide/ssr',
|
||||
label: 'HTTP Client',
|
||||
children: [
|
||||
{
|
||||
label: 'Overview',
|
||||
path: 'guide/http',
|
||||
contentPath: 'guide/http/overview',
|
||||
},
|
||||
{
|
||||
label: 'Setting up HttpClient',
|
||||
path: 'guide/http/setup',
|
||||
contentPath: 'guide/http/setup',
|
||||
},
|
||||
{
|
||||
label: 'Making requests',
|
||||
path: 'guide/http/making-requests',
|
||||
contentPath: 'guide/http/making-requests',
|
||||
},
|
||||
{
|
||||
label: 'Intercepting requests and responses',
|
||||
path: 'guide/http/interceptors',
|
||||
contentPath: 'guide/http/interceptors',
|
||||
},
|
||||
{
|
||||
label: 'Testing',
|
||||
path: 'guide/http/testing',
|
||||
contentPath: 'guide/http/testing',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Build-time prerendering',
|
||||
path: 'guide/prerendering',
|
||||
contentPath: 'guide/prerendering',
|
||||
},
|
||||
{
|
||||
label: 'Hydration',
|
||||
path: 'guide/hydration',
|
||||
contentPath: 'guide/hydration',
|
||||
},
|
||||
{
|
||||
label: 'Deferrable views',
|
||||
path: 'guide/defer',
|
||||
contentPath: 'guide/defer',
|
||||
},
|
||||
{
|
||||
label: 'Image Optimization',
|
||||
path: 'guide/image-optimization',
|
||||
contentPath: 'guide/image-optimization',
|
||||
label: 'Performance',
|
||||
children: [
|
||||
{
|
||||
label: 'Deferrable views',
|
||||
path: 'guide/defer',
|
||||
contentPath: 'guide/defer',
|
||||
},
|
||||
{
|
||||
label: 'Image Optimization',
|
||||
path: 'guide/image-optimization',
|
||||
contentPath: 'guide/image-optimization',
|
||||
},
|
||||
{
|
||||
label: 'Server-side Rendering',
|
||||
path: 'guide/ssr',
|
||||
contentPath: 'guide/ssr',
|
||||
},
|
||||
{
|
||||
label: 'Build-time prerendering',
|
||||
path: 'guide/prerendering',
|
||||
contentPath: 'guide/prerendering',
|
||||
},
|
||||
{
|
||||
label: 'Hydration',
|
||||
path: 'guide/hydration',
|
||||
contentPath: 'guide/hydration',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Testing',
|
||||
@@ -589,81 +604,6 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Security',
|
||||
path: 'guide/security',
|
||||
contentPath: 'guide/security',
|
||||
},
|
||||
{
|
||||
label: 'Pipes',
|
||||
children: [
|
||||
{
|
||||
label: 'Overview',
|
||||
path: 'guide/pipes',
|
||||
contentPath: 'guide/pipes/overview',
|
||||
},
|
||||
{
|
||||
label: 'Using a pipe in a template',
|
||||
path: 'guide/pipes/template',
|
||||
contentPath: 'guide/pipes/template',
|
||||
},
|
||||
{
|
||||
label: 'Custom pipes',
|
||||
path: 'guide/pipes/transform-data',
|
||||
contentPath: 'guide/pipes/transform-data',
|
||||
},
|
||||
{
|
||||
label: 'Pipe precedence in expressions',
|
||||
path: 'guide/pipes/precedence',
|
||||
contentPath: 'guide/pipes/precedence',
|
||||
},
|
||||
{
|
||||
label: 'Change detection with pipes',
|
||||
path: 'guide/pipes/change-detection',
|
||||
contentPath: 'guide/pipes/change-detection',
|
||||
},
|
||||
{
|
||||
label: 'Unwrapping data from an observable',
|
||||
path: 'guide/pipes/unwrapping-data-observables',
|
||||
contentPath: 'guide/pipes/unwrapping-data-observables',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Http Client',
|
||||
children: [
|
||||
{
|
||||
label: 'Overview',
|
||||
path: 'guide/http',
|
||||
contentPath: 'guide/http/overview',
|
||||
},
|
||||
{
|
||||
label: 'Setting up HttpClient',
|
||||
path: 'guide/http/setup',
|
||||
contentPath: 'guide/http/setup',
|
||||
},
|
||||
{
|
||||
label: 'Making requests',
|
||||
path: 'guide/http/making-requests',
|
||||
contentPath: 'guide/http/making-requests',
|
||||
},
|
||||
{
|
||||
label: 'Intercepting requests and responses',
|
||||
path: 'guide/http/interceptors',
|
||||
contentPath: 'guide/http/interceptors',
|
||||
},
|
||||
{
|
||||
label: 'Security',
|
||||
path: 'guide/http/security',
|
||||
contentPath: 'guide/http/security',
|
||||
},
|
||||
{
|
||||
label: 'Testing',
|
||||
path: 'guide/http/testing',
|
||||
contentPath: 'guide/http/testing',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Animations',
|
||||
children: [
|
||||
@@ -694,11 +634,6 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Custom Elements',
|
||||
path: 'guide/elements',
|
||||
contentPath: 'guide/elements',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -830,9 +765,9 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
|
||||
contentPath: 'best-practices/style-guide',
|
||||
},
|
||||
{
|
||||
label: 'Keeping up-to-date',
|
||||
path: 'update',
|
||||
contentPath: 'best-practices/update',
|
||||
label: 'Security',
|
||||
path: 'best-practices/security',
|
||||
contentPath: 'guide/security', // Have not refactored due to build issues
|
||||
},
|
||||
{
|
||||
label: 'Accessibility',
|
||||
@@ -864,6 +799,11 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Keeping up-to-date',
|
||||
path: 'update',
|
||||
contentPath: 'best-practices/update',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -1349,6 +1289,81 @@ const REFERENCE_SUB_NAVIGATION_DATA: NavigationItem[] = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Concepts',
|
||||
children: [
|
||||
{
|
||||
label: 'Overview',
|
||||
path: 'reference/concepts',
|
||||
contentPath: 'reference/concepts/overview',
|
||||
},
|
||||
{
|
||||
label: 'NgModule',
|
||||
children: [
|
||||
{
|
||||
label: 'Overview',
|
||||
path: 'guide/ngmodules',
|
||||
contentPath: 'guide/ngmodules/overview',
|
||||
},
|
||||
{
|
||||
label: 'JS Modules vs NgModules',
|
||||
path: 'guide/ngmodules/vs-jsmodule',
|
||||
contentPath: 'guide/ngmodules/vs-jsmodule',
|
||||
},
|
||||
{
|
||||
label: 'Launching your app with a root module',
|
||||
path: 'guide/ngmodules/bootstrapping',
|
||||
contentPath: 'guide/ngmodules/bootstrapping',
|
||||
},
|
||||
{
|
||||
label: 'Sharing NgModules',
|
||||
path: 'guide/ngmodules/sharing',
|
||||
contentPath: 'guide/ngmodules/sharing',
|
||||
},
|
||||
{
|
||||
label: 'Frequently used NgModules',
|
||||
path: 'guide/ngmodules/frequent',
|
||||
contentPath: 'guide/ngmodules/frequent',
|
||||
},
|
||||
{
|
||||
label: 'Feature modules',
|
||||
path: 'guide/ngmodules/feature-modules',
|
||||
contentPath: 'guide/ngmodules/feature-modules',
|
||||
},
|
||||
{
|
||||
label: 'Types of feature modules',
|
||||
path: 'guide/ngmodules/module-types',
|
||||
contentPath: 'guide/ngmodules/module-types',
|
||||
},
|
||||
{
|
||||
label: 'Providing dependencies',
|
||||
path: 'guide/ngmodules/providers',
|
||||
contentPath: 'guide/ngmodules/providers',
|
||||
},
|
||||
{
|
||||
label: 'Singleton services',
|
||||
path: 'guide/ngmodules/singleton-services',
|
||||
contentPath: 'guide/ngmodules/singleton-services',
|
||||
},
|
||||
{
|
||||
label: 'Lazy-loading feature modules',
|
||||
path: 'guide/ngmodules/lazy-loading',
|
||||
contentPath: 'guide/ngmodules/lazy-loading',
|
||||
},
|
||||
{
|
||||
label: 'NgModule API',
|
||||
path: 'guide/ngmodules/api',
|
||||
contentPath: 'guide/ngmodules/api',
|
||||
},
|
||||
{
|
||||
label: 'NgModule FAQs',
|
||||
path: 'guide/ngmodules/faq',
|
||||
contentPath: 'guide/ngmodules/faq',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const FOOTER_NAVIGATION_DATA: NavigationItem[] = [
|
||||
|
||||
@@ -27,6 +27,7 @@ copy_to_directory(
|
||||
"//adev/src/content/introduction",
|
||||
"//adev/src/content/introduction/essentials",
|
||||
"//adev/src/content/reference",
|
||||
"//adev/src/content/reference/concepts",
|
||||
"//adev/src/content/reference/configs",
|
||||
"//adev/src/content/reference/errors",
|
||||
"//adev/src/content/reference/extended-diagnostics",
|
||||
|
||||
|
Before Width: | Height: | Size: 2.2 MiB After Width: | Height: | Size: 2.2 MiB |
|
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 254 KiB After Width: | Height: | Size: 254 KiB |
|
Before Width: | Height: | Size: 2.0 MiB After Width: | Height: | Size: 2.0 MiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 724 KiB After Width: | Height: | Size: 724 KiB |
|
Before Width: | Height: | Size: 745 KiB After Width: | Height: | Size: 745 KiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ These events are shown in the following table.
|
||||
| [`ChildActivationEnd`](api/router/ChildActivationEnd) | Triggered when the Router finishes activating a route's children. |
|
||||
| [`ActivationEnd`](api/router/ActivationEnd) | Triggered when the Router finishes activating a route. |
|
||||
| [`NavigationEnd`](api/router/NavigationEnd) | Triggered when navigation ends successfully. |
|
||||
| [`NavigationCancel`](api/router/NavigationCancel) | Triggered when navigation is canceled. This can happen when a Route Guard returns false during navigation, or redirects by returning a `UrlTree`. |
|
||||
| [`NavigationCancel`](api/router/NavigationCancel) | Triggered when navigation is canceled. This can happen when a Route Guard returns false during navigation, or redirects by returning a `UrlTree` or `RedirectCommand`. |
|
||||
| [`NavigationError`](api/router/NavigationError) | Triggered when navigation fails due to an unexpected error. |
|
||||
| [`Scroll`](api/router/Scroll) | Represents a scrolling event. |
|
||||
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
# Signal inputs
|
||||
|
||||
Signal inputs allow values to be bound from parent components.
|
||||
Those values are exposed using a `Signal` and can change during the lifecycle of your component.
|
||||
|
||||
HELPFUL: Signal inputs are currently in [developer preview](/guide/releases#developer-preview).
|
||||
|
||||
Angular supports two variants of inputs:
|
||||
|
||||
**Optional inputs**
|
||||
Inputs are optional by default, unless you use `input.required`.
|
||||
You can specify an explicit initial value, or Angular will use `undefined` implicitly.
|
||||
|
||||
**Required inputs**
|
||||
Required inputs always have a value of the given input type.
|
||||
They are declared using the `input.required` function.
|
||||
|
||||
```typescript
|
||||
import {Component, input} from '@angular/core';
|
||||
|
||||
@Component({...})
|
||||
export class MyComp {
|
||||
// optional
|
||||
firstName = input<string>(); // InputSignal<string|undefined>
|
||||
age = input(0); // InputSignal<number>
|
||||
|
||||
// required
|
||||
lastName = input.required<string>(); // InputSignal<string>
|
||||
}
|
||||
```
|
||||
|
||||
An input is automatically recognized by Angular whenever you use the `input` or `input.required` functions as initializer of class members.
|
||||
|
||||
## Aliasing an input
|
||||
|
||||
Angular uses the class member name as the name of the input.
|
||||
You can alias inputs to change their public name to be different.
|
||||
|
||||
```typescript
|
||||
class StudentDirective {
|
||||
age = input(0, {alias: 'studentAge'});
|
||||
}
|
||||
```
|
||||
|
||||
This allows users to bind to your input using `[studentAge]`, while inside your component you can access the input values using `this.age`.
|
||||
|
||||
## Using in templates
|
||||
|
||||
Signal inputs are read-only signals.
|
||||
As with signals declared via `signal()`, you access the current value of the input by calling the input signal.
|
||||
|
||||
```html
|
||||
<p>First name: {{firstName()}}</p>
|
||||
<p>Last name: {{lastName()}}</p>
|
||||
```
|
||||
|
||||
This access to the value is captured in reactive contexts and can notify active consumers, like Angular itself, whenever the input value changes.
|
||||
|
||||
An input signal in practice is a trivial extension of signals that you know from [the signals guide](/guide/signals).
|
||||
|
||||
```typescript
|
||||
export class InputSignal<T> extends Signal<T> { ... }`.
|
||||
```
|
||||
|
||||
## Deriving values
|
||||
|
||||
As with signals, you can derive values from inputs using `computed`.
|
||||
|
||||
```typescript
|
||||
import {Component, input, computed} from '@angular/core';
|
||||
|
||||
@Component({...})
|
||||
export class MyComp {
|
||||
age = input(0);
|
||||
|
||||
// age multiplied by two.
|
||||
ageMultiplied = computed(() => this.age() * 2);
|
||||
}
|
||||
```
|
||||
|
||||
Computed signals memoize values.
|
||||
See more details in the [dedicated section for computed](/guide/signals#computed-signals).
|
||||
|
||||
## Monitoring changes
|
||||
|
||||
With signal inputs, users can leverage the `effect` function.
|
||||
The function will execute whenever the input changes.
|
||||
|
||||
Consider the following example.
|
||||
The new value is printed to the console whenever the `firstName` input changes.
|
||||
|
||||
```typescript
|
||||
import {input, effect} from '@angular/core';
|
||||
|
||||
class MyComp {
|
||||
firstName = input.required<string>();
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
console.log(this.firstName());
|
||||
});
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `console.log` function is invoked every time the `firstName` input changes.
|
||||
This will happen as soon as `firstName` is available, and for subsequent changes during the lifetime of `MyComp`.
|
||||
|
||||
## Value transforms
|
||||
|
||||
You may want to coerce or parse input values without changing the meaning of the input.
|
||||
Transforms convert the raw value from parent templates to the expected input type.
|
||||
Transforms should be [pure functions](https://en.wikipedia.org/wiki/Pure_function).
|
||||
|
||||
```typescript
|
||||
class MyComp {
|
||||
disabled = input(false, {
|
||||
transform: (value: boolean|string) => typeof value === 'string' ? value === '' : value,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
In the example above, you are declaring an input named `disabled` that is accepting values of type `boolean` and `string`.
|
||||
This is captured by the explicit parameter type of `value` in the `transform` option.
|
||||
These values are then parsed to a `boolean` with the transform, resulting in booleans.
|
||||
|
||||
That way, you are only dealing with `boolean` inside your component when calling `this.disabled()`, while users of your component can pass an empty string as a shorthand to mark your component as disabled.
|
||||
|
||||
```html
|
||||
<my-custom-comp disabled>
|
||||
```
|
||||
|
||||
|
||||
IMPORTANT:Do not use transforms if they change the meaning of the input, or if they are [impure](https://en.wikipedia.org/wiki/Pure_function#Impure_functions).
|
||||
Instead, use `computed` for transformations with different meaning, or an `effect` for impure code that should run whenever the input changes.
|
||||
|
||||
## Why should we use signal inputs and not `@Input()`?
|
||||
|
||||
Signal inputs are a reactive alternative to decorator-based `@Input()`.
|
||||
|
||||
In comparison to decorator-based `@Input`, signal inputs provide numerous benefits:
|
||||
|
||||
1. Signal inputs are more **type safe**:
|
||||
<br/>• Required inputs do not require initial values, or tricks to tell TypeScript that an input _always_ has a value.
|
||||
<br/>• Transforms are automatically checked to match the accepted input values.
|
||||
2. Signal inputs, when used in templates, will **automatically** mark `OnPush` components as dirty.
|
||||
3. Values can be easily **derived** whenever an input changes using `computed`.
|
||||
4. Easier and more local monitoring of inputs using `effect` instead of `ngOnChanges` or setters.
|
||||
@@ -0,0 +1,138 @@
|
||||
# Model inputs
|
||||
|
||||
**Model inputs** are a special type of input that enable a component to propagate new values
|
||||
back to another component.
|
||||
|
||||
HELPFUL: Model inputs are currently in [developer preview](/guide/releases#developer-preview).
|
||||
|
||||
When creating a component, you can define a model input similarly to how you create a standard
|
||||
input.
|
||||
|
||||
```typescript
|
||||
import {Component, model, input} from '@angular/core';
|
||||
|
||||
@Component({...})
|
||||
export class CustomCheckbox {
|
||||
// This is a model input.
|
||||
checked = model(false);
|
||||
|
||||
// This is a standard input.
|
||||
disabled = input(false);
|
||||
}
|
||||
```
|
||||
|
||||
Both types of input allow someone to bind a value into the property. However, **model inputs allow
|
||||
the component author to write values into the property**.
|
||||
|
||||
In other respects, you can use model inputs the same way you use standard inputs. You can read the
|
||||
value by calling the signal function, including in reactive contexts like `computed` and `effect`.
|
||||
|
||||
```typescript
|
||||
import {Component, model, input} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'custom-checkbox',
|
||||
template: '<div (click)="toggle()"> ... </div>',
|
||||
})
|
||||
export class CustomCheckbox {
|
||||
checked = model(false);
|
||||
disabled = input(false);
|
||||
|
||||
toggle() {
|
||||
// While standard inputs are read-only, you can write directly to model inputs.
|
||||
this.checked.set(!this.checked());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When a component writes a new value into a model input, Angular can propagate the new value back
|
||||
to the component that is binding a value into that input. This is called **two-way binding** because
|
||||
values can flow in both directions.
|
||||
|
||||
## Two-way binding with signals
|
||||
|
||||
You can bind a writable signal to a model input.
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
...,
|
||||
// `checked` is a model input.
|
||||
// The parenthesis-inside-square-brackets syntax (aka "banana-in-a-box") creates a two-way binding
|
||||
template: '<custom-checkbox [(checked)]="isAdmin" />',
|
||||
})
|
||||
export class UserProfile {
|
||||
protected isAdmin = signal(false);
|
||||
}
|
||||
```
|
||||
|
||||
In the above example, the `CustomCheckbox` can write values into its `checked` model input, which
|
||||
then propagates those values back to the `isAdmin` signal in `UserProfile`. This binding keeps that
|
||||
values of `checked` and `isAdmin` in sync. Notice that the binding passes the `isAdmin` signal
|
||||
itself, not the _value_ of the signal.
|
||||
|
||||
## Two-way binding with plain properties
|
||||
|
||||
You can bind a plain JavaScript property to a model input.
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
...,
|
||||
// `checked` is a model input.
|
||||
// The parenthesis-inside-square-brackets syntax (aka "banana-in-a-box") creates a two-way binding
|
||||
template: '<custom-checkbox [(checked)]="isAdmin" />',
|
||||
})
|
||||
export class UserProfile {
|
||||
protected isAdmin = false;
|
||||
}
|
||||
```
|
||||
|
||||
In the example above, the `CustomCheckbox` can write values into its `checked` model input, which
|
||||
then propagates those values back to the `isAdmin` property in `UserProfile`. This binding keeps
|
||||
that values of `checked` and `isAdmin` in sync.
|
||||
|
||||
## Implicit `change` events
|
||||
|
||||
When you declare a model input in a component or directive, Angular automatically creates a
|
||||
corresponding [output](/guide/inputs-outputs) for that model. The output's name is the model
|
||||
input's name suffixed with "Change".
|
||||
|
||||
```typescript
|
||||
@Directive({...})
|
||||
export class CustomCheckbox {
|
||||
// This automatically creates an output named "checkedChange".
|
||||
// Can be subscribed to using `(checkedChange)="handler()"` in the template.
|
||||
checked = model(false);
|
||||
}
|
||||
```
|
||||
|
||||
Angular emits this change event whenever you write a new value into the model input by calling
|
||||
its `set` or `update` methods.
|
||||
|
||||
## Customizing model inputs
|
||||
|
||||
You can mark a model input as required or provide an alias in the same way as a
|
||||
[standard input](/guide/signal-inputs).
|
||||
|
||||
Model inputs do not support input transforms.
|
||||
|
||||
## Differences between `model()` and `input()`
|
||||
|
||||
Both `input()` and `model()` functions are ways to define signal-based inputs in Angular, but they
|
||||
differ in a few ways:
|
||||
1. `model()` defines **both** an input and an output. The output's name is always the name of the
|
||||
input suffixed with `Change` to support two-way bindings. It will be up to the consumer of your
|
||||
directive to decide if they want to use just the input, just the output, or both.
|
||||
2. `ModelSignal` is a `WritableSignal` which means that its value can be changed from anywhere
|
||||
using the `set` and `update` methods. When a new value is assigned, the `ModelSignal` will emit
|
||||
to its output. This is different from `InputSignal` which is read-only and can only be changed
|
||||
through the template.
|
||||
3. Model inputs do not support input transforms while signal inputs do.
|
||||
|
||||
## When to use model inputs
|
||||
|
||||
Use model inputs in components that exist to modify a value based on user interaction.
|
||||
Custom form controls, such as a date picker or combobox, should use model inputs for their
|
||||
primary value.
|
||||
|
||||
Avoid using model inputs as a convenience to avoid introducing an additional class property for
|
||||
containing local state.
|
||||
@@ -0,0 +1,9 @@
|
||||
load("@npm//@angular/docs:index.bzl", "generate_guides")
|
||||
|
||||
generate_guides(
|
||||
name = "concepts",
|
||||
srcs = glob([
|
||||
"*.md",
|
||||
]),
|
||||
visibility = ["//adev:__subpackages__"],
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
# Concepts
|
||||
|
||||
<docs-card-container>
|
||||
<docs-card title="NgModules" link="Learn more" href="guide/ngmodules">
|
||||
NgModules is a concept that commonly used in architecture v16 and earlier to help configure the injector and the compiler and help organize related things together.
|
||||
</docs-card>
|
||||
</docs-card-container>
|
||||
@@ -1,6 +1,6 @@
|
||||
# The MIT License
|
||||
|
||||
Copyright (c) 2010-2023 Google LLC. https://angular.dev/license
|
||||
Copyright (c) 2010-2024 Google LLC. https://angular.dev/license
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
@@ -9,21 +9,21 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^17.2.0",
|
||||
"@angular/common": "^17.2.0",
|
||||
"@angular/compiler": "^17.2.0",
|
||||
"@angular/core": "^17.2.0",
|
||||
"@angular/forms": "^17.2.0",
|
||||
"@angular/platform-browser": "^17.2.0",
|
||||
"@angular/router": "^17.2.0",
|
||||
"@angular/animations": "^17.3.0-rc",
|
||||
"@angular/common": "^17.3.0-rc",
|
||||
"@angular/compiler": "^17.3.0-rc",
|
||||
"@angular/core": "^17.3.0-rc",
|
||||
"@angular/forms": "^17.3.0-rc",
|
||||
"@angular/platform-browser": "^17.3.0-rc",
|
||||
"@angular/router": "^17.3.0-rc",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^17.2.0",
|
||||
"@angular/cli": "^17.2.0",
|
||||
"@angular/compiler-cli": "^17.2.0",
|
||||
"@angular-devkit/build-angular": "^17.3.0-rc",
|
||||
"@angular/cli": "^17.3.0-rc",
|
||||
"@angular/compiler-cli": "^17.3.0-rc",
|
||||
"@types/jasmine": "~5.1.0",
|
||||
"@types/node": "^16.11.35",
|
||||
"copyfiles": "^2.4.1",
|
||||
|
||||
@@ -32,7 +32,7 @@ Note: Look for alerts like this one, which call out steps that may only be for y
|
||||
The lessons in this tutorial create an Angular app that lists houses for rent and shows the details of individual houses.
|
||||
This app uses features that are common to many Angular apps.
|
||||
|
||||
<img alt="Output of heroes dashboard" src="assets/images/tutorials/first-app/homes-app-landing-page.png">
|
||||
<img alt="Output of homes landing page" src="assets/images/tutorials/first-app/homes-app-landing-page.png">
|
||||
|
||||
## Local development environment
|
||||
|
||||
|
||||
@@ -9,19 +9,19 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/common": "^17.2.0",
|
||||
"@angular/compiler": "^17.2.0",
|
||||
"@angular/core": "^17.2.0",
|
||||
"@angular/forms": "^17.2.0",
|
||||
"@angular/platform-browser": "^17.2.0",
|
||||
"@angular/common": "^17.3.0-rc",
|
||||
"@angular/compiler": "^17.3.0-rc",
|
||||
"@angular/core": "^17.3.0-rc",
|
||||
"@angular/forms": "^17.3.0-rc",
|
||||
"@angular/platform-browser": "^17.3.0-rc",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^17.2.0",
|
||||
"@angular/cli": "^17.2.0",
|
||||
"@angular/compiler-cli": "^17.2.0",
|
||||
"@angular-devkit/build-angular": "^17.3.0-rc",
|
||||
"@angular/cli": "^17.3.0-rc",
|
||||
"@angular/compiler-cli": "^17.3.0-rc",
|
||||
"typescript": "~5.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,20 +9,20 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/common": "^17.2.0",
|
||||
"@angular/compiler": "^17.2.0",
|
||||
"@angular/core": "^17.2.0",
|
||||
"@angular/forms": "^17.2.0",
|
||||
"@angular/platform-browser": "^17.2.0",
|
||||
"@angular/router": "^17.2.0",
|
||||
"@angular/common": "^17.3.0-rc",
|
||||
"@angular/compiler": "^17.3.0-rc",
|
||||
"@angular/core": "^17.3.0-rc",
|
||||
"@angular/forms": "^17.3.0-rc",
|
||||
"@angular/platform-browser": "^17.3.0-rc",
|
||||
"@angular/router": "^17.3.0-rc",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^17.2.0",
|
||||
"@angular/cli": "^17.2.0",
|
||||
"@angular/compiler-cli": "^17.2.0",
|
||||
"@angular-devkit/build-angular": "^17.3.0-rc",
|
||||
"@angular/cli": "^17.3.0-rc",
|
||||
"@angular/compiler-cli": "^17.3.0-rc",
|
||||
"typescript": "~5.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ In this activity you will learn to use constructor-based dependency injection.
|
||||
|
||||
<hr>
|
||||
|
||||
To inject a service or some other injectable resource into your componenet use the following syntax:
|
||||
To inject a service or some other injectable resource into your component use the following syntax:
|
||||
|
||||
<docs-code language="ts" highlight="[3]">
|
||||
@Component({...})
|
||||
|
||||
@@ -9,22 +9,22 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^17.2.0",
|
||||
"@angular/cdk": "^17.2.0",
|
||||
"@angular/common": "^17.2.0",
|
||||
"@angular/compiler": "^17.2.0",
|
||||
"@angular/core": "^17.2.0",
|
||||
"@angular/forms": "^17.2.0",
|
||||
"@angular/material": "^17.2.0",
|
||||
"@angular/platform-browser": "^17.2.0",
|
||||
"@angular/animations": "^17.3.0-rc",
|
||||
"@angular/cdk": "^17.3.0-rc",
|
||||
"@angular/common": "^17.3.0-rc",
|
||||
"@angular/compiler": "^17.3.0-rc",
|
||||
"@angular/core": "^17.3.0-rc",
|
||||
"@angular/forms": "^17.3.0-rc",
|
||||
"@angular/material": "^17.3.0-rc",
|
||||
"@angular/platform-browser": "^17.3.0-rc",
|
||||
"rxjs": "~7.8.0",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^17.2.0",
|
||||
"@angular/cli": "^17.2.0",
|
||||
"@angular/compiler-cli": "^17.2.0",
|
||||
"@angular-devkit/build-angular": "^17.3.0-rc",
|
||||
"@angular/cli": "^17.3.0-rc",
|
||||
"@angular/compiler-cli": "^17.3.0-rc",
|
||||
"typescript": "~5.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,246 +0,0 @@
|
||||
{
|
||||
"root": true,
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"*.ts"
|
||||
],
|
||||
"parserOptions": {
|
||||
"project": [
|
||||
"tsconfig.json",
|
||||
"tests/e2e/tsconfig.json"
|
||||
],
|
||||
"createDefaultProgram": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:@angular-eslint/template/process-inline-templates"
|
||||
],
|
||||
"plugins": [
|
||||
"@typescript-eslint",
|
||||
"@angular-eslint",
|
||||
"eslint-plugin-jsdoc",
|
||||
"eslint-plugin-import",
|
||||
"eslint-plugin-jsdoc",
|
||||
"eslint-plugin-prefer-arrow"
|
||||
],
|
||||
"rules": {
|
||||
"@angular-eslint/component-class-suffix": "error",
|
||||
"@angular-eslint/component-selector": [
|
||||
"error",
|
||||
{
|
||||
"prefix": "aio",
|
||||
"style": "kebab-case",
|
||||
"type": "element"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/contextual-lifecycle": "error",
|
||||
"@angular-eslint/directive-class-suffix": "error",
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
"prefix": "aio",
|
||||
"style": "camelCase",
|
||||
"type": "attribute"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/no-conflicting-lifecycle": "error",
|
||||
"@angular-eslint/no-host-metadata-property": "off",
|
||||
"@angular-eslint/no-input-rename": "error",
|
||||
"@angular-eslint/no-inputs-metadata-property": "error",
|
||||
"@angular-eslint/no-output-native": "error",
|
||||
"@angular-eslint/no-output-on-prefix": "error",
|
||||
"@angular-eslint/no-output-rename": "error",
|
||||
"@angular-eslint/no-outputs-metadata-property": "error",
|
||||
"@angular-eslint/use-lifecycle-interface": "error",
|
||||
"@angular-eslint/use-pipe-transform-interface": "error",
|
||||
"@typescript-eslint/adjacent-overload-signatures": "error",
|
||||
"@typescript-eslint/array-type": "off",
|
||||
"@typescript-eslint/ban-types": "error",
|
||||
"@typescript-eslint/consistent-type-assertions": "error",
|
||||
"@typescript-eslint/dot-notation": "error",
|
||||
"@typescript-eslint/explicit-member-accessibility": "off",
|
||||
"@typescript-eslint/interface-name-prefix": "off",
|
||||
"@typescript-eslint/member-delimiter-style": [
|
||||
"error",
|
||||
{
|
||||
"singleline": {
|
||||
"delimiter": "comma",
|
||||
"requireLast": false
|
||||
}
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/member-ordering": "off",
|
||||
"@typescript-eslint/naming-convention": "off",
|
||||
"@typescript-eslint/no-empty-function": "off",
|
||||
"@typescript-eslint/no-empty-interface": "error",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/no-inferrable-types": [
|
||||
"error",
|
||||
{
|
||||
"ignoreParameters": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-misused-new": "error",
|
||||
"@typescript-eslint/no-namespace": "error",
|
||||
"@typescript-eslint/no-non-null-assertion": "error",
|
||||
"@typescript-eslint/no-parameter-properties": "off",
|
||||
"@typescript-eslint/no-shadow": [
|
||||
"error"
|
||||
],
|
||||
"@typescript-eslint/no-unused-expressions": "error",
|
||||
"@typescript-eslint/no-use-before-define": "off",
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"@typescript-eslint/prefer-for-of": "error",
|
||||
"@typescript-eslint/prefer-function-type": "error",
|
||||
"@typescript-eslint/prefer-namespace-keyword": "error",
|
||||
"@typescript-eslint/quotes": [
|
||||
"error",
|
||||
"single",
|
||||
{
|
||||
"avoidEscape": true
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"@typescript-eslint/triple-slash-reference": [
|
||||
"error",
|
||||
{
|
||||
"lib": "always",
|
||||
"path": "always",
|
||||
"types": "prefer-import"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/type-annotation-spacing": "error",
|
||||
"@typescript-eslint/unified-signatures": "error",
|
||||
"arrow-body-style": "error",
|
||||
"arrow-parens": "off",
|
||||
"comma-dangle": "off",
|
||||
"complexity": "off",
|
||||
"constructor-super": "error",
|
||||
"curly": "error",
|
||||
"eol-last": "error",
|
||||
"eqeqeq": [
|
||||
"error",
|
||||
"smart"
|
||||
],
|
||||
"guard-for-in": "error",
|
||||
"id-denylist": [
|
||||
"error",
|
||||
"any",
|
||||
"Number",
|
||||
"number",
|
||||
"String",
|
||||
"string",
|
||||
"Boolean",
|
||||
"boolean",
|
||||
"Undefined",
|
||||
"undefined"
|
||||
],
|
||||
"id-match": "error",
|
||||
"import/no-deprecated": "warn",
|
||||
"indent": "off",
|
||||
"jsdoc/check-alignment": "error",
|
||||
"jsdoc/no-types": "error",
|
||||
"max-classes-per-file": "off",
|
||||
"max-len": [
|
||||
"error",
|
||||
120
|
||||
],
|
||||
"new-parens": "error",
|
||||
"no-bitwise": "error",
|
||||
"no-caller": "error",
|
||||
"no-cond-assign": "error",
|
||||
"no-console": [
|
||||
"error",
|
||||
{
|
||||
"allow": [
|
||||
"log",
|
||||
"warn",
|
||||
"error"
|
||||
]
|
||||
}
|
||||
],
|
||||
"no-debugger": "error",
|
||||
"no-empty": "off",
|
||||
"no-empty-function": "off",
|
||||
"no-eval": "error",
|
||||
"no-fallthrough": "error",
|
||||
"no-invalid-this": "off",
|
||||
"no-multiple-empty-lines": "off",
|
||||
"no-new-wrappers": "error",
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"message": "Please import directly from 'rxjs' instead",
|
||||
"name": "rxjs/Rx"
|
||||
}
|
||||
],
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
{
|
||||
"message": "Don't keep jasmine focus methods.",
|
||||
"selector": "CallExpression[callee.name=/^(fdescribe|fit)$/]"
|
||||
}
|
||||
],
|
||||
"no-shadow": "off",
|
||||
"no-tabs": "error",
|
||||
"no-throw-literal": "error",
|
||||
"no-trailing-spaces": "error",
|
||||
"no-undef-init": "error",
|
||||
"no-underscore-dangle": "off",
|
||||
"no-unsafe-finally": "error",
|
||||
"no-unused-expressions": "off",
|
||||
"no-unused-labels": "error",
|
||||
"no-use-before-define": "off",
|
||||
"no-var": "error",
|
||||
"object-shorthand": "error",
|
||||
"one-var": [
|
||||
"error",
|
||||
"never"
|
||||
],
|
||||
"prefer-arrow/prefer-arrow-functions": "off",
|
||||
"prefer-const": "error",
|
||||
"quote-props": [
|
||||
"error",
|
||||
"as-needed"
|
||||
],
|
||||
"quotes": "off",
|
||||
"radix": "error",
|
||||
"sort-keys": "off",
|
||||
"space-before-function-paren": [
|
||||
"error",
|
||||
{
|
||||
"anonymous": "never",
|
||||
"asyncArrow": "always",
|
||||
"named": "never"
|
||||
}
|
||||
],
|
||||
"use-isnan": "error",
|
||||
"valid-typeof": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": [
|
||||
"*.html"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:@angular-eslint/template/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"@angular-eslint/template/alt-text": "error",
|
||||
"@angular-eslint/template/elements-content": "error",
|
||||
"@angular-eslint/template/label-has-associated-control": "error",
|
||||
"@angular-eslint/template/table-scope": "error",
|
||||
"@angular-eslint/template/valid-aria": "error",
|
||||
"@angular-eslint/template/click-events-have-key-events": "error",
|
||||
"@angular-eslint/template/eqeqeq": "off",
|
||||
"@angular-eslint/template/mouse-events-have-key-events": "error",
|
||||
"@angular-eslint/template/no-autofocus": "error",
|
||||
"@angular-eslint/template/no-distracting-elements": "error",
|
||||
"@angular-eslint/template/no-positive-tabindex": "error"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
engine-strict = true
|
||||
@@ -1,11 +1,11 @@
|
||||
load("@aio_npm//@angular-devkit/architect-cli:index.bzl", "architect", "architect_test")
|
||||
load("@npm//@angular-devkit/architect-cli:index.bzl", "architect", "architect_test")
|
||||
load("@build_bazel_rules_nodejs//:index.bzl", "copy_to_bin", "npm_package_bin")
|
||||
load("//tools:defaults.bzl", "nodejs_binary")
|
||||
load("@aspect_bazel_lib//lib:copy_to_directory.bzl", "copy_to_directory")
|
||||
load(":local_packages_util.bzl", "link_local_packages", "substitute_local_package_deps")
|
||||
load("@bazel_skylib//rules:common_settings.bzl", "bool_flag", "string_flag")
|
||||
load("//aio/scripts:local_server_test.bzl", "local_server_test")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load(":aio_targets.bzl", "aio_test")
|
||||
load("@bazel_skylib//lib:collections.bzl", "collections")
|
||||
|
||||
@@ -91,7 +91,7 @@ npm_package_bin(
|
||||
tags = [
|
||||
"requires-network",
|
||||
],
|
||||
tool = "@aio_npm//dgeni/bin:dgeni",
|
||||
tool = "@npm//dgeni/bin:dgeni",
|
||||
toolchains = [
|
||||
"@npm//@angular/build-tooling/bazel/git-toolchain:current_git_toolchain",
|
||||
],
|
||||
@@ -148,29 +148,29 @@ APPLICATION_DEPS = [
|
||||
":stackblitz",
|
||||
":example-zips",
|
||||
"//aio/src/generated:ngsw-config",
|
||||
"@aio_npm//@angular-devkit/build-angular",
|
||||
"@aio_npm//@angular-eslint/builder",
|
||||
"@aio_npm//@angular/animations",
|
||||
"@aio_npm//@angular/cdk",
|
||||
"@aio_npm//@angular/cli",
|
||||
"@aio_npm//@angular/common",
|
||||
"@aio_npm//@angular/compiler",
|
||||
"@aio_npm//@angular/compiler-cli",
|
||||
"@aio_npm//@angular/core",
|
||||
"@aio_npm//@angular/elements",
|
||||
"@aio_npm//@angular/forms",
|
||||
"@aio_npm//@angular/material",
|
||||
"@aio_npm//@angular/platform-browser",
|
||||
"@aio_npm//@angular/platform-browser-dynamic",
|
||||
"@aio_npm//@angular/router",
|
||||
"@aio_npm//@angular/service-worker",
|
||||
"@aio_npm//@types/lunr",
|
||||
"@aio_npm//@types/trusted-types",
|
||||
"@aio_npm//lunr",
|
||||
"@aio_npm//rxjs",
|
||||
"@aio_npm//safevalues",
|
||||
"@aio_npm//tslib",
|
||||
"@aio_npm//zone.js",
|
||||
"@npm//@angular-devkit/build-angular",
|
||||
"@npm//@angular-eslint/builder",
|
||||
"@npm//@angular/animations",
|
||||
"@npm//@angular/cdk",
|
||||
"@npm//@angular/cli",
|
||||
"@npm//@angular/common",
|
||||
"@npm//@angular/compiler",
|
||||
"@npm//@angular/compiler-cli",
|
||||
"@npm//@angular/core",
|
||||
"@npm//@angular/elements",
|
||||
"@npm//@angular/forms",
|
||||
"@npm//@angular/material",
|
||||
"@npm//@angular/platform-browser",
|
||||
"@npm//@angular/platform-browser-dynamic",
|
||||
"@npm//@angular/router",
|
||||
"@npm//@angular/service-worker",
|
||||
"@npm//@types/lunr",
|
||||
"@npm//@types/trusted-types",
|
||||
"@npm//lunr",
|
||||
"@npm//rxjs",
|
||||
"@npm//safevalues",
|
||||
"@npm//tslib",
|
||||
"@npm//zone.js",
|
||||
]
|
||||
|
||||
# All sources, specs, and config files required to test the docs app
|
||||
@@ -183,16 +183,16 @@ TEST_FILES = APPLICATION_FILES + [
|
||||
|
||||
# Dependencies required to test the docs app
|
||||
TEST_DEPS = APPLICATION_DEPS + [
|
||||
"@aio_npm//@angular/build-tooling/bazel/browsers/chromium",
|
||||
"@aio_npm//@types/jasmine",
|
||||
"@aio_npm//@types/node",
|
||||
"@aio_npm//assert",
|
||||
"@aio_npm//jasmine",
|
||||
"@aio_npm//jasmine-core",
|
||||
"@aio_npm//karma-chrome-launcher",
|
||||
"@aio_npm//karma-coverage",
|
||||
"@aio_npm//karma-jasmine",
|
||||
"@aio_npm//karma-jasmine-html-reporter",
|
||||
"@npm//@angular/build-tooling/bazel/browsers/chromium",
|
||||
"@npm//@types/jasmine",
|
||||
"@npm//@types/node",
|
||||
"@npm//assert",
|
||||
"@npm//jasmine",
|
||||
"@npm//jasmine-core",
|
||||
"@npm//karma-chrome-launcher",
|
||||
"@npm//karma-coverage",
|
||||
"@npm//karma-jasmine",
|
||||
"@npm//karma-jasmine-html-reporter",
|
||||
"//aio/tools:windows-chromium-path",
|
||||
]
|
||||
|
||||
@@ -202,12 +202,12 @@ E2E_FILES = APPLICATION_FILES + glob(["tests/e2e/**"])
|
||||
|
||||
# Dependencies required to run the e2e tests
|
||||
E2E_DEPS = APPLICATION_DEPS + [
|
||||
"@aio_npm//@angular/build-tooling/bazel/browsers/chromium",
|
||||
"@aio_npm//@types/jasmine",
|
||||
"@aio_npm//@types/node",
|
||||
"@aio_npm//jasmine-spec-reporter",
|
||||
"@aio_npm//protractor",
|
||||
"@aio_npm//ts-node",
|
||||
"@npm//@angular/build-tooling/bazel/browsers/chromium",
|
||||
"@npm//@types/jasmine",
|
||||
"@npm//@types/node",
|
||||
"@npm//jasmine-spec-reporter",
|
||||
"@npm//protractor",
|
||||
"@npm//ts-node",
|
||||
"//aio/tools:windows-chromium-path",
|
||||
]
|
||||
|
||||
@@ -322,7 +322,7 @@ architect_test(
|
||||
# (presumably after AIO migration to Bazel)
|
||||
flaky = True,
|
||||
toolchains = [
|
||||
"@aio_npm//@angular/build-tooling/bazel/browsers/chromium:toolchain_alias",
|
||||
"@npm//@angular/build-tooling/bazel/browsers/chromium:toolchain_alias",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
# Angular documentation project (https://angular.io)
|
||||
|
||||
Everything in this folder is part of the documentation project. This includes:
|
||||
|
||||
* the web site for displaying the documentation.
|
||||
* the dgeni configuration for converting source files to rendered files that can be viewed in the web site.
|
||||
* the tooling for setting up examples for development; and generating live-example and zip files from the examples.
|
||||
|
||||
<a name="developer-tasks"></a>
|
||||
## Developer tasks
|
||||
|
||||
We use [Yarn](https://yarnpkg.com) to manage the dependencies and development tasks. Behind the scenes, [Bazel](https://bazel.build/) is used to build targets and run tests.
|
||||
You should run all these tasks from the `angular/aio` folder.
|
||||
Here are the most important tasks you might need to use:
|
||||
|
||||
* `yarn` - install all the dependencies.
|
||||
* `yarn build` - create a development build of the application.
|
||||
* `yarn build-prod` - create a production build of the application.
|
||||
* `yarn build-local` - same as `build`, but uses locally built Angular packages from source code rather than from npm.
|
||||
* `yarn start` - run a development web server that watches, rebuilds, and reloads the page when there are changes to the source code or docs.
|
||||
* `yarn start-local` - same as `start`, but uses local Angular packages.
|
||||
* `yarn test` - run all the unit tests for the doc-viewer once.
|
||||
* `yarn test-local` - similar to `test`, but tests against locally built Angular packages.
|
||||
* `yarn test-and-watch` - watch all the source files for the doc-viewer, and run all the unit tests when any change.
|
||||
* `yarn e2e` - run all the e2e tests for the doc-viewer.
|
||||
* `yarn e2e-local` - similar to `e2e`, but tests against locally built Angular packages.
|
||||
* `yarn lint` - check that the doc-viewer code follows our style rules.
|
||||
|
||||
* `yarn docs-watch` - similar to `start`, but only watches for docs changes and uses a faster, low-fidelity build ideal for quick editing.
|
||||
* `yarn docs-test` - run the unit tests for the doc generation code.
|
||||
* `yarn docs-lint` - check that the doc gen code follows our style rules.
|
||||
|
||||
* `yarn create-example` - create a new example directory containing initial source files.
|
||||
* `yarn example-playground <exampleName>` - set up a playground to manually test an example combined with its boilerplate files
|
||||
- `--local` - link locally build Angular packages as deps
|
||||
- `--watch` - update the playground when sources change
|
||||
|
||||
* `yarn example-e2e` - run all e2e tests for examples. Available options:
|
||||
- `--local`: run e2e tests against locally built Angular packages.
|
||||
- `--filter=foo`: limit e2e tests to those containing the word "foo".
|
||||
- `--exclude=bar`: exclude e2e tests containing the word "bar".
|
||||
|
||||
> **Note for Windows users**
|
||||
>
|
||||
> The underlying Bazel build requires creating [symbolic links](https://en.wikipedia.org/wiki/Symbolic_link) (see [here](./tools/examples/README.md#symlinked-node_modules) for details). On Windows, this requires to either have [Developer Mode enabled](https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10) (supported on Windows 10 or newer) or run the setup commands as administrator.
|
||||
|
||||
## Using ServiceWorker locally
|
||||
|
||||
Running `yarn start` (even when explicitly targeting production mode) does not set up the
|
||||
ServiceWorker. If you want to test the ServiceWorker locally, you can use `yarn build` and then
|
||||
serve the files with `yarn http-server ../dist/bin/aio/build -p 4200`.
|
||||
|
||||
|
||||
## Guide to authoring
|
||||
|
||||
There are two types of content in the documentation:
|
||||
|
||||
* **API docs**: descriptions of all that make up the Angular platform, such as the modules, classes, interfaces or decorators.
|
||||
API docs are generated directly from the source code.
|
||||
The source code is contained in TypeScript files, located in the `angular/packages` folder.
|
||||
Each API item may have a preceding comment, which contains JSDoc style tags and content.
|
||||
The content is written in markdown. To generate docs, each package's files need to be explicitly included in the [packages/BUILD.bazel](../packages/BUILD.bazel) file under the `files_for_docgen` target.
|
||||
|
||||
* **Other content**: guides, tutorials, and other marketing material.
|
||||
All other content is written using markdown in text files, located in the `angular/aio/content` folder.
|
||||
More specifically, there are sub-folders that contain particular types of content: guides, tutorial and marketing.
|
||||
|
||||
* **Code examples**: code examples need to be testable to ensure their accuracy.
|
||||
Also, our examples have a specific look and feel and allow the user to copy the source code. For larger
|
||||
examples they are rendered in a tabbed interface (e.g. template, HTML, and TypeScript on separate
|
||||
tabs). Additionally, some are live examples, which provide links where the code can be edited, executed, and/or downloaded. For details on working with code examples, please read the [Code snippets](https://angular.io/guide/docs-style-guide#code-snippets), [Source code markup](https://angular.io/guide/docs-style-guide#source-code-markup), and [Live examples](https://angular.io/guide/docs-style-guide#live-examples) pages of the [Authors Style Guide](https://angular.io/guide/docs-style-guide).
|
||||
|
||||
We use the [dgeni](https://github.com/angular/dgeni) tool to convert these files into docs that can be viewed in the doc-viewer.
|
||||
|
||||
The [Authors Style Guide](https://angular.io/guide/docs-style-guide) prescribes guidelines for
|
||||
writing guide pages, explains how to use the documentation classes and components, and how to markup sample source code to produce code snippets.
|
||||
|
||||
### Generating the complete docs
|
||||
|
||||
Running the `yarn build` or `yarn start` tasks will automatically generate the docs. This will process all the source files (API and other),
|
||||
extracting the documentation and generating JSON files that can be consumed by the doc-viewer.
|
||||
|
||||
### Partial doc generation for editors
|
||||
|
||||
Full doc generation can take up to one minute. That's too slow for efficient document creation and editing.
|
||||
|
||||
You can make small changes in a smart editor that displays formatted markdown:
|
||||
>In VS Code, _Cmd-K, V_ opens markdown preview in side pane; _Cmd-B_ toggles left sidebar
|
||||
|
||||
You also want to see those changes displayed properly in the doc viewer
|
||||
with a quick, edit/view cycle time.
|
||||
|
||||
For this purpose, use the `yarn docs-watch` task, which watches for changes to source files and only
|
||||
re-processes the files necessary to generate the docs that are related to the file that has changed.
|
||||
Since this task takes shortcuts, it is much faster (often less than 1 second) but it won't produce full
|
||||
fidelity content. For example, links to other docs and code examples may not render correctly. This is
|
||||
most particularly noticed in links to other docs and in the embedded examples, which may not always render
|
||||
correctly.
|
||||
|
||||
The general setup is as follows:
|
||||
|
||||
* Open a terminal, ensure the dependencies are installed, then start the doc-viewer:
|
||||
|
||||
```bash
|
||||
yarn docs-watch
|
||||
```
|
||||
|
||||
* A browser will open automatically at https://localhost:4200/. Navigate to the document on which you want to work.
|
||||
|
||||
* Make changes to the page's associated doc or example files. Every time a file is saved, the doc will
|
||||
be regenerated, the app will rebuild and the page will reload.
|
||||
|
||||
* If you get a build error complaining about examples or any other odd behavior, be sure to consult
|
||||
the [Authors Style Guide](https://angular.io/guide/docs-style-guide).
|
||||
@@ -1,42 +0,0 @@
|
||||
# Update the angular.io app
|
||||
|
||||
The dependencies of the angular.io app (including Angular, Angular Material and Angular CLI) are automatically updated using [Renovate](https://renovatebot.com/).
|
||||
|
||||
However, it is useful to periodically also manually update the app to more closely match (in file layout, configs, etc.) what a new Angular CLI app would look like.
|
||||
This is typically only needed once for each new major Angular version.
|
||||
|
||||
Since angular.io is an Angular CLI app, we can take advantage of `ng update` to apply migrations.
|
||||
|
||||
Follow these steps to align the angular.io app with new CLI apps.
|
||||
|
||||
> **Note:**
|
||||
> The following steps assume that the related Angular dependencies have already been updated in [aio/package.json](./package.json) (for example, automatically by Renovate).
|
||||
|
||||
> **Note:**
|
||||
> All commands shown below are expected to be executed from inside the [aio/](./) directory (unless specified otherwise).
|
||||
|
||||
- Determine (for example, by examining git history) what is the last versions for which this process was performed.
|
||||
These will be referred to as `<FROM_VERSION_*>`.
|
||||
If you can't determine these, use arbitrary versions, such as the previous major version.
|
||||
|
||||
- Run the following commands to automatically apply any available migrations to the project:
|
||||
```sh
|
||||
# Ensure dependencies are installed.
|
||||
yarn install
|
||||
|
||||
# Migrate project to new versions.
|
||||
yarn ng update @angular/cli --allow-dirty --migrate-only --from=<FROM_VERSION_CLI>
|
||||
yarn ng update @angular/core --allow-dirty --migrate-only --from=<FROM_VERSION_ANGULAR>
|
||||
yarn ng update @angular/material --allow-dirty --migrate-only --from=<FROM_VERSION_MATERIAL>
|
||||
```
|
||||
|
||||
> **Note:**
|
||||
> Depending on the number of changes generated from each `ng update` command, it might make sense to create a separate commit for each update.
|
||||
|
||||
- Inspect [package.json](./package.json) to determine what is the current version of Angular CLI (i.e. `@angular/cli`) used in the app.
|
||||
This will be referred to as `<TO_VERSION_CLI>`.
|
||||
|
||||
- Use the [angular-cli-diff](https://github.com/cexbrayat/angular-cli-diff) repository to discover more changes (which are not automatically applied via `ng update` migrations) between Angular CLI apps of different versions.
|
||||
Visit https://github.com/cexbrayat/angular-cli-diff/compare/<FROM_VERSION_CLI>...<TO_VERSION_CLI>, inspect the changes between the two versions and apply the ones that make sense to the angular.io source code.
|
||||
|
||||
- Commit all changes and [submit a pull request](../CONTRIBUTING.md#submit-pr).
|
||||
@@ -1,4 +1,4 @@
|
||||
load("@aio_npm//@angular-devkit/architect-cli:index.bzl", "architect_test")
|
||||
load("@npm//@angular-devkit/architect-cli:index.bzl", "architect_test")
|
||||
|
||||
def aio_test(name, data, args, **kwargs):
|
||||
architect_test(
|
||||
@@ -10,7 +10,7 @@ def aio_test(name, data, args, **kwargs):
|
||||
"CHROME_BIN": "../$(CHROMIUM)",
|
||||
},
|
||||
toolchains = [
|
||||
"@aio_npm//@angular/build-tooling/bazel/browsers/chromium:toolchain_alias",
|
||||
"@npm//@angular/build-tooling/bazel/browsers/chromium:toolchain_alias",
|
||||
],
|
||||
**kwargs
|
||||
)
|
||||
|
||||
@@ -1,264 +0,0 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"cli": {
|
||||
"packageManager": "yarn",
|
||||
"warnings": {
|
||||
"typescriptMismatch": false
|
||||
},
|
||||
"analytics": false,
|
||||
"cache": {
|
||||
// Disable build caching as the cache folder will just be dropped
|
||||
// when run under Bazel sandboxed execution.
|
||||
"enabled": false
|
||||
}
|
||||
},
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"site": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:application": {
|
||||
"strict": true
|
||||
},
|
||||
"@schematics/angular:component": {
|
||||
"inlineStyle": true,
|
||||
"style": "scss"
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "aio",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": ["zone.js"],
|
||||
"ngswConfigPath": "src/generated/ngsw-config.json",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"webWorkerTsConfig": "tsconfig.worker.json",
|
||||
"optimization": {
|
||||
"fonts": {
|
||||
"inline": false
|
||||
},
|
||||
"scripts": true,
|
||||
"styles": {
|
||||
"inlineCritical": false,
|
||||
"minify": true
|
||||
}
|
||||
},
|
||||
"outputHashing": "all",
|
||||
"sourceMap": true,
|
||||
"namedChunks": true,
|
||||
"assets": [
|
||||
// Architect on Windows has difficulty service assets files within a
|
||||
// Bazel runfiles symlink forest. Using "followSymlinks" seems to
|
||||
// fix this. Note that the assets below don't need this workaround as
|
||||
// symlinked tree artifacts pointing to the output tree don't appear
|
||||
// to have the same issue.
|
||||
{
|
||||
"followSymlinks": true,
|
||||
"input": "src/assets",
|
||||
"glob": "**/*",
|
||||
"output": "/assets/"
|
||||
},
|
||||
{
|
||||
"followSymlinks": true,
|
||||
"input": "src/",
|
||||
"glob": "**/pwa-manifest.json",
|
||||
"output": "/"
|
||||
},
|
||||
{
|
||||
"followSymlinks": true,
|
||||
"input": "src/",
|
||||
"glob": "**/google385281288605d160.html",
|
||||
"output": "/"
|
||||
},
|
||||
{
|
||||
"input": "stackblitz/generated",
|
||||
"output": "generated",
|
||||
"glob": "**"
|
||||
},
|
||||
{
|
||||
"input": "example-zips/generated",
|
||||
"output": "generated",
|
||||
"glob": "**"
|
||||
},
|
||||
{
|
||||
"input": "dgeni/generated",
|
||||
"output": "generated",
|
||||
"glob": "**"
|
||||
},
|
||||
{
|
||||
"input": "dgeni-fast/generated",
|
||||
"output": "generated",
|
||||
"glob": "**"
|
||||
}
|
||||
],
|
||||
"styles": [
|
||||
"src/styles/main.scss",
|
||||
{
|
||||
"inject": false,
|
||||
"input": "src/styles/custom-themes/dark-theme.scss",
|
||||
"bundleName": "dark-theme"
|
||||
},
|
||||
{
|
||||
"inject": false,
|
||||
"input": "src/styles/custom-themes/light-theme.scss",
|
||||
"bundleName": "light-theme"
|
||||
}
|
||||
],
|
||||
"scripts": [],
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "850kb",
|
||||
"maximumError": "1mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "2kb",
|
||||
"maximumError": "4kb"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
"next": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.next.ts"
|
||||
}
|
||||
],
|
||||
"serviceWorker": true
|
||||
},
|
||||
"rc": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.rc.ts"
|
||||
}
|
||||
],
|
||||
"serviceWorker": true
|
||||
},
|
||||
"stable": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.stable.ts"
|
||||
}
|
||||
],
|
||||
"serviceWorker": true
|
||||
},
|
||||
"archive": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.archive.ts"
|
||||
}
|
||||
],
|
||||
"serviceWorker": true
|
||||
},
|
||||
"ci": {
|
||||
"progress": false
|
||||
},
|
||||
"development": {
|
||||
"buildOptimizer": false,
|
||||
"optimization": false,
|
||||
"outputHashing": "none",
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "stable"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"headers": {
|
||||
// Keep in sync with the `firebase.json` file.
|
||||
// Note: Unlike in `firebase.json` we omit the `report-uri` for development.
|
||||
"Content-Security-Policy": "require-trusted-types-for 'script'; trusted-types angular angular#bundler angular#unsafe-bypass aio#analytics google#safe goog#html"
|
||||
}
|
||||
},
|
||||
"configurations": {
|
||||
"next": {
|
||||
"browserTarget": "site:build:next"
|
||||
},
|
||||
"rc": {
|
||||
"browserTarget": "site:build:rc"
|
||||
},
|
||||
"stable": {
|
||||
"browserTarget": "site:build:stable"
|
||||
},
|
||||
"archive": {
|
||||
"browserTarget": "site:build:archive"
|
||||
},
|
||||
"ci": {
|
||||
"browserTarget": "site:build:ci"
|
||||
},
|
||||
"development": {
|
||||
"browserTarget": "site:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "site:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"polyfills": ["zone.js", "zone.js/testing"],
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"webWorkerTsConfig": "tsconfig.worker.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"assets": ["src/assets", "src/pwa-manifest.json", "src/google385281288605d160.html"],
|
||||
"styles": [
|
||||
"src/styles/main.scss",
|
||||
{
|
||||
"inject": false,
|
||||
"input": "src/styles/custom-themes/dark-theme.scss",
|
||||
"bundleName": "dark-theme"
|
||||
},
|
||||
{
|
||||
"inject": false,
|
||||
"input": "src/styles/custom-themes/light-theme.scss",
|
||||
"bundleName": "light-theme"
|
||||
}
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-eslint/builder:lint",
|
||||
"options": {
|
||||
"lintFilePatterns": [
|
||||
"src/!(generated)/**/*.ts",
|
||||
"src/!(generated)/**/*.html",
|
||||
"tests/**/*.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "tests/e2e/protractor.conf.js",
|
||||
"devServerTarget": "site:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"ci": {
|
||||
"devServerTarget": "site:serve:ci"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"branchName": "refs/heads/main",
|
||||
"sha": "01f78129bcbea278a959d365dee7f34666d5e341"
|
||||
"sha": "76a340c895b3fb124b07aa8a12f78202f5b8ea98"
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
"command": "ng serve [project]",
|
||||
"shortDescription": "Builds and serves your application, rebuilding on file changes.",
|
||||
"aliases": [
|
||||
"dev",
|
||||
"s"
|
||||
],
|
||||
"deprecated": false,
|
||||
@@ -10,7 +11,7 @@
|
||||
{
|
||||
"name": "allowed-hosts",
|
||||
"type": "array",
|
||||
"description": "List of hosts that are allowed to access the dev server."
|
||||
"description": "List of hosts that are allowed to access the dev server. This option has no effect when using the 'application' or other esbuild-based builders."
|
||||
},
|
||||
{
|
||||
"name": "browser-target",
|
||||
@@ -35,7 +36,7 @@
|
||||
"name": "disable-host-check",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Don't verify connected clients are part of allowed hosts."
|
||||
"description": "Don't verify connected clients are part of allowed hosts. This option has no effect when using the 'application' or other esbuild-based builders."
|
||||
},
|
||||
{
|
||||
"name": "force-esbuild",
|
||||
@@ -105,7 +106,7 @@
|
||||
{
|
||||
"name": "public-host",
|
||||
"type": "string",
|
||||
"description": "The URL that the browser client (or live-reload client, if enabled) should use to connect to the development server. Use for a complex dev server setup, such as one with reverse proxies."
|
||||
"description": "The URL that the browser client (or live-reload client, if enabled) should use to connect to the development server. Use for a complex dev server setup, such as one with reverse proxies. This option has no effect when using the 'application' or other esbuild-based builders."
|
||||
},
|
||||
{
|
||||
"name": "serve-path",
|
||||
|
||||
@@ -206,7 +206,7 @@ def docs_example(name, test = True, test_tags = [], test_exec_properties = {}, f
|
||||
data = [
|
||||
":%s" % name,
|
||||
YARN_LABEL,
|
||||
"@aio_npm//@angular/build-tooling/bazel/browsers/chromium",
|
||||
"@npm//@angular/build-tooling/bazel/browsers/chromium",
|
||||
"//aio/tools/examples:run-example-e2e",
|
||||
"//aio/tools:windows-chromium-path",
|
||||
# We install the whole node modules for runtime deps of e2e tests
|
||||
@@ -229,7 +229,7 @@ def docs_example(name, test = True, test_tags = [], test_exec_properties = {}, f
|
||||
"CHROMEDRIVER_BIN": "$(CHROMEDRIVER)",
|
||||
},
|
||||
toolchains = [
|
||||
"@aio_npm//@angular/build-tooling/bazel/browsers/chromium:toolchain_alias",
|
||||
"@npm//@angular/build-tooling/bazel/browsers/chromium:toolchain_alias",
|
||||
],
|
||||
exec_properties = test_exec_properties,
|
||||
flaky = flaky,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
load("//aio/content/examples:examples.bzl", "docs_example")
|
||||
load("@aio_npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
load("@npm//@angular/build-tooling/bazel/remote-execution:index.bzl", "ENABLE_NETWORK")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
@description
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2023 Google LLC. https://angular.io/license
|
||||
Copyright (c) 2010-2024 Google LLC. https://angular.io/license
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files \(the "Software"\), to deal
|
||||
|
||||
@@ -1313,6 +1313,12 @@
|
||||
},
|
||||
"name": "JSDayIE"
|
||||
},
|
||||
{
|
||||
"date": {
|
||||
"start": "2024-01-30"
|
||||
},
|
||||
"name": "THAT Conf TX"
|
||||
},
|
||||
{
|
||||
"date": {
|
||||
"start": "2024-02-29"
|
||||
@@ -1325,16 +1331,52 @@
|
||||
},
|
||||
"name": "ng-conf"
|
||||
},
|
||||
{
|
||||
"date": {
|
||||
"start": "2024-04-10"
|
||||
},
|
||||
"name": "Cloud Next"
|
||||
},
|
||||
{
|
||||
"date": {
|
||||
"start": "2024-05-24"
|
||||
},
|
||||
"name": "NG Belgrade Conf"
|
||||
},
|
||||
{
|
||||
"date": {
|
||||
"start": "2024-05-30"
|
||||
},
|
||||
"name": "Craft"
|
||||
},
|
||||
{
|
||||
"date": {
|
||||
"start": "2024-06-12"
|
||||
},
|
||||
"name": "RenderATL"
|
||||
},
|
||||
{
|
||||
"date": {
|
||||
"start": "2024-06-13"
|
||||
},
|
||||
"name": "JSNation"
|
||||
},
|
||||
{
|
||||
"date": {
|
||||
"start": "2024-06-27"
|
||||
},
|
||||
"name": "dotJS"
|
||||
},
|
||||
{
|
||||
"date": {
|
||||
"start": "2024-07-29"
|
||||
},
|
||||
"name": "THAT Conf WI"
|
||||
},
|
||||
{
|
||||
"date": {
|
||||
"start": "2024-10-10"
|
||||
},
|
||||
"name": "NG-DE"
|
||||
}
|
||||
]
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"rules": {
|
||||
".read": false,
|
||||
".write": false,
|
||||
|
||||
"events": {
|
||||
".read": true,
|
||||
".write": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,287 +0,0 @@
|
||||
{
|
||||
// Docs on Firebase hosting configuration: https://firebase.google.com/docs/hosting/full-config
|
||||
"hosting": {
|
||||
"target": "aio",
|
||||
"public": "dist",
|
||||
"cleanUrls": true,
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ADDING REDIRECTS
|
||||
//
|
||||
// In order to permanently redirect a URL to a new address, add an new entry in the `redirects`
|
||||
// list:
|
||||
// ```json
|
||||
// {"type": 301, "source": "/old/url", "destination": "/new/url"}
|
||||
// ```
|
||||
//
|
||||
// This will cause the server to redirect any clients that visit `/old/url` to `/new/url`. At
|
||||
// the same time, the ServiceWorker will be automatically configured to let requests for
|
||||
// `/old/url` pass through to the server (instead of serving them locally with the cached
|
||||
// `index.html`).
|
||||
//
|
||||
// In addition, you should also add a corresponding testcase in `URLS_TO_REDIRECT.txt` (inside
|
||||
// `tests/deployment/shared/`) to ensure that the new redirect behaves as expected.
|
||||
//
|
||||
// NOTE 1:
|
||||
// On clients that still have an old ServiceWorker version installed the next time they visit a
|
||||
// recently redirected URL, the ServiceWorker will not yet know to let the request pass through
|
||||
// to the server (and get redirected). In such cases, those clients will get a "Page not found"
|
||||
// error, as the app tries to load `generated/docs/old/url.json` (which no longer exists). Such
|
||||
// errors would typically go away after reloading the page (since the ServiceWorker will have
|
||||
// updated to the latest version in the background).
|
||||
//
|
||||
// This temporary error is usually not a concern, because it only affects clients with an old
|
||||
// ServiceWorker version at the time of the first visit and it goes away on reload. If, however,
|
||||
// it is important to avoid this error for a specific redirect, you can add an additional
|
||||
// redirect entry for the requested JSON file:
|
||||
// ```json
|
||||
// {"type": 302, "source": "/generated/docs/old/url.json", "destination": "/generated/docs/new/url.json"}
|
||||
// ```
|
||||
//
|
||||
// In order to avoid bloating the redirects config, it is recommended to remove this additional
|
||||
// entry after some time has passed, at which point the large majority of existing clients will
|
||||
// have switched to an updated ServiceWorker version.
|
||||
//
|
||||
// NOTE 2:
|
||||
// If you decide to add the additional entry described above, keep in mind that the JSON file
|
||||
// URL is derived from the page URL by prepending `/generated/docs`, replacing any upper-case
|
||||
// letters with the corresponding lower-case ones followed by an underscore (`_`) and appending
|
||||
// `.json`.
|
||||
//
|
||||
// For example, the page URL `/api/forms/FormBuilder` would result in requesting the JSON file
|
||||
// `/generated/docs/api/forms/f_ormb_uilder.json`.
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
"redirects": [
|
||||
// A random bad indexed page that used `api/api`
|
||||
{"type": 301, "source": "/api/api/:rest*", "destination": "/api/:rest*"},
|
||||
|
||||
// Guide renames/removals
|
||||
{"type": 301, "source": "/docs/*/latest/cli-quickstart.html", "destination": "/start"},
|
||||
{"type": 301, "source": "/docs/*/latest/glossary.html", "destination": "/guide/glossary"},
|
||||
{"type": 301, "source": "/docs/*/latest/quickstart.html", "destination": "/start"},
|
||||
{"type": 301, "source": "/docs/*/latest/guide/server-communication.html", "destination": "/guide/understanding-communicating-with-http"},
|
||||
{"type": 301, "source": "/docs/*/latest/guide/style-guide.html", "destination": "/guide/styleguide"},
|
||||
{"type": 301, "source": "/guide/bazel", "destination": "https://github.com/angular/angular/blob/main/packages/bazel/docs/BAZEL_SCHEMATICS.md"},
|
||||
{"type": 301, "source": "/guide", "destination": "/docs"},
|
||||
{"type": 301, "source": "/guide/cli-quickstart", "destination": "/start"},
|
||||
{"type": 301, "source": "/guide/i18n", "destination": "/guide/i18n-overview"},
|
||||
{"type": 301, "source": "/guide/service-worker-getstart", "destination": "/guide/service-worker-getting-started"},
|
||||
{"type": 301, "source": "/guide/service-worker-comm", "destination": "/guide/service-worker-communications"},
|
||||
{"type": 301, "source": "/guide/service-worker-configref", "destination": "/guide/service-worker-config"},
|
||||
{"type": 301, "source": "/guide/webpack", "destination": "https://v5.angular.io/guide/webpack"},
|
||||
{"type": 301, "source": "/guide/http", "destination": "/guide/understanding-communicating-with-http"},
|
||||
{"type": 301, "source": "/guide/setup", "destination": "/guide/setup-local"},
|
||||
{"type": 301, "source": "/guide/setup-systemjs-anatomy", "destination": "/guide/file-structure"},
|
||||
{"type": 301, "source": "/guide/change-log", "destination": "https://github.com/angular/angular/blob/main/CHANGELOG.md"},
|
||||
{"type": 301, "source": "/guide/quickstart", "destination": "/start"},
|
||||
{"type": 301, "source": "/getting-started", "destination": "/start"},
|
||||
{"type": 301, "source": "/getting-started/:rest*", "destination": "/start/:rest*"},
|
||||
{"type": 301, "source": "/guide/displaying-data", "destination": "/start#template-syntax"},
|
||||
{"type": 301, "source": "/guide/ivy", "destination": "https://v12.angular.io/guide/ivy"},
|
||||
{"type": 301, "source": "/guide/updating-to-version-10", "destination": "https://v10.angular.io/guide/updating-to-version-10"},
|
||||
{"type": 301, "source": "/guide/updating-to-version-11", "destination": "https://v11.angular.io/guide/updating-to-version-11"},
|
||||
{"type": 301, "source": "/guide/updating-to-version-12", "destination": "https://v12.angular.io/guide/updating-to-version-12"},
|
||||
{"type": 301, "source": "/guide/updating-to-version-13", "destination": "https://v13.angular.io/guide/update-to-latest-version"},
|
||||
{"type": 301, "source": "/guide/set-document-title", "destination": "/guide/router#setting-the-page-title"},
|
||||
|
||||
// Update Angular guide refactor to name individual update topics.
|
||||
// This redirects the generic link to the current version's topic.
|
||||
// The destination value must be updated with each new major version.
|
||||
// When changing this value, be sure to update or add the corresponding
|
||||
// test in angular/aio/tests/deployment/shared/URLS_TO_REDIRECT.txt
|
||||
{"type": 301, "source": "/guide/update-to-latest-version", "destination": "/guide/update-to-version-17"},
|
||||
|
||||
// Renaming of Getting Started topics
|
||||
{"type": 301, "source": "/start/data", "destination": "/start/start-data"},
|
||||
{"type": 301, "source": "/start/deployment", "destination": "/start/start-deployment"},
|
||||
{"type": 301, "source": "/start/forms", "destination": "/start/start-forms"},
|
||||
{"type": 301, "source": "/start/routing", "destination": "/start/start-routing"},
|
||||
|
||||
// Tutorials moved to subdirectories
|
||||
{"type": 301, "source": "/tutorial/index", "destination": "/tutorial/tour-of-heroes/index"},
|
||||
{"type": 301, "source": "/tutorial/toh-pt0", "destination": "/tutorial/tour-of-heroes/toh-pt0"},
|
||||
{"type": 301, "source": "/tutorial/toh-pt1", "destination": "/tutorial/tour-of-heroes/toh-pt1"},
|
||||
{"type": 301, "source": "/tutorial/toh-pt2", "destination": "/tutorial/tour-of-heroes/toh-pt2"},
|
||||
{"type": 301, "source": "/tutorial/toh-pt3", "destination": "/tutorial/tour-of-heroes/toh-pt3"},
|
||||
{"type": 301, "source": "/tutorial/toh-pt4", "destination": "/tutorial/tour-of-heroes/toh-pt4"},
|
||||
{"type": 301, "source": "/tutorial/toh-pt5", "destination": "/tutorial/tour-of-heroes/toh-pt5"},
|
||||
{"type": 301, "source": "/tutorial/toh-pt6", "destination": "/tutorial/tour-of-heroes/toh-pt6"},
|
||||
|
||||
// some top level guide pages on old site were moved below the guide folder
|
||||
{"type": 301, "source": "/styleguide", "destination": "/guide/styleguide"},
|
||||
{"type": 301, "source": "/docs/styleguide", "destination": "/guide/styleguide"},
|
||||
|
||||
// news is now blog
|
||||
{"type": 301, "source": "/news*", "destination": "https://blog.angular.io/"},
|
||||
|
||||
// cookbook guides were moved (and sometime renamed or removed)
|
||||
{"type": 301, "source": "/docs/*/latest/cookbook", "destination": "/docs"},
|
||||
{"type": 301, "source": "/docs/*/latest/cookbook/", "destination": "/docs"},
|
||||
{"type": 301, "source": "/docs/*/latest/cookbook/index.html", "destination": "/docs"},
|
||||
{"type": 301, "source": "/**/cookbook/ts-to-js*", "destination": "https://v2.angular.io/docs/ts/latest/cookbook/ts-to-js.html"},
|
||||
{"type": 301, "source": "/docs/*/latest/cookbook/a1-a2-quick-reference.html", "destination": "/guide/ajs-quick-reference"},
|
||||
{"type": 301, "source": "/docs/*/latest/cookbook/component-communication.html", "destination": "/guide/component-interaction"},
|
||||
{"type": 301, "source": "/docs/*/latest/cookbook/dependency-injection.html", "destination": "/guide/dependency-injection-in-action"},
|
||||
{"type": 301, "source": "/docs/*/latest/cookbook/:cookbook.html", "destination": "/guide/:cookbook"},
|
||||
|
||||
// Forms related code was moved from the `common` to `forms` package (and NgFor was renamed to NgForOf)
|
||||
{"type": 301, "source": "/**/NgFor-*", "destination": "/api/common/NgForOf"},
|
||||
{"type": 301, "source": "/**/api/common/index/MaxLengthValidator-*", "destination": "/api/forms/MaxLengthValidator"},
|
||||
{"type": 301, "source": "/**/api/common/ControlGroup*", "destination": "/api/forms/FormGroup"},
|
||||
{"type": 301, "source": "/**/api/common/Control*", "destination": "/api/forms/FormControl"},
|
||||
{"type": 301, "source": "/**/api/common/SelectControlValueAccessor-*", "destination": "/api/forms/SelectControlValueAccessor"},
|
||||
{"type": 301, "source": "/**/api/common/NgModel", "destination": "/api/forms/NgModel"},
|
||||
|
||||
// `@angular/http` package was removed, and new `HttpClient` APIs are available under `@angular/common/http` package
|
||||
{"type": 301, "source": "/api/http", "destination": "/guide/deprecations#http"},
|
||||
{"type": 301, "source": "/api/http/**", "destination": "/guide/deprecations#http"},
|
||||
|
||||
// Animations moves, renames and removals
|
||||
{"type": 301, "source": "/api/animate/:rest*", "destination": "/api/animations/:rest*"},
|
||||
// AnimationStateDeclarationMetadata was removed
|
||||
{"type": 301, "source": "/**/AnimationStateDeclarationMetadata*", "destination": "/api/animations"},
|
||||
// `AnimationDriver` was moved to the `animations/browser` package
|
||||
{"type": 301, "source": "/api/platform-browser/AnimationDriver", "destination": "/api/animations/browser/AnimationDriver"},
|
||||
|
||||
// The `testing` package was renamed to `core/testing`
|
||||
{"type": 301, "source": "/api/testing/:api-*", "destination": "/api/core/testing/:api"},
|
||||
|
||||
// CORE_DIRECTIVES & PLATFORM_PIPES were removed and are now in the CommonModule
|
||||
{"type": 301, "source": "/**/CORE_DIRECTIVES*", "destination": "/api/common/CommonModule"},
|
||||
{"type": 301, "source": "/**/PLATFORM_PIPES*", "destination": "/api/common/CommonModule"},
|
||||
|
||||
// DirectiveMetadata is now covered by the Directive decorator
|
||||
{"type": 301, "source": "/**/DirectiveMetadata-*", "destination": "/api/core/Directive"},
|
||||
|
||||
// OptionalMetadata is now covered by the Optional decorator
|
||||
{"type": 301, "source": "/**/OptionalMetadata-*", "destination": "/api/core/Optional"},
|
||||
|
||||
// HTTP_PROVIDERS was removed and is now provided in HttpModule
|
||||
{"type": 301, "source": "/**/HTTP_PROVIDERS*", "destination": "/api/http/HttpModule"},
|
||||
|
||||
// URLs that use the old scheme of adding the type to the end (e.g. `SomeClass-class`)
|
||||
// (Exclude disambiguated URLs that might be suffixed with `-\d+` (e.g. `SomeClass-1`))
|
||||
{"type": 301, "source": "/api/:package/:api-@(class|decorator|directive|function|interface|let|pipe|type|type-alias|var)", "destination": "/api/:package/:api"},
|
||||
{"type": 301, "source": "/api/:package/testing/index/:api", "destination": "/api/:package/testing/:api"},
|
||||
{"type": 301, "source": "/api/:package/testing/:api-@(class|decorator|directive|function|interface|let|pipe|type|type-alias|var)", "destination": "/api/:package/testing/:api"},
|
||||
{"type": 301, "source": "/api/upgrade/:package/index/:api", "destination": "/api/upgrade/:package/:api"},
|
||||
{"type": 301, "source": "/api/upgrade/:package/:api-@(class|decorator|directive|function|interface|let|pipe|type|type-alias|var)", "destination": "/api/upgrade/:package/:api"},
|
||||
|
||||
// URLs that use the old scheme before we moved the docs to the angular/angular repo
|
||||
{"type": 301, "source": "/docs/*/latest", "destination": "/docs"},
|
||||
{"type": 301, "source": "/docs/*/latest/api/", "destination": "/api"},
|
||||
{"type": 301, "source": "/docs/*/latest/api/testing/:api-*", "destination": "/api/core/testing/:api"},
|
||||
{"type": 301, "source": "/docs/*/latest/api/:package/:api-*", "destination": "/api/:package/:api"},
|
||||
{"type": 301, "source": "/docs/*/latest/api/:package/index/:api-*", "destination": "/api/:package/:api"},
|
||||
{"type": 301, "source": "/docs/*/latest/api/:package/testing", "destination": "/api/:package/testing"},
|
||||
{"type": 301, "source": "/docs/*/latest/api/:package/testing/index/:api-*", "destination": "/api/:package/testing/:api"},
|
||||
{"type": 301, "source": "/docs/*/latest/api/platform-browser/animations/index/:api-*", "destination": "/api/platform-browser/animations/:api"},
|
||||
{"type": 301, "source": "/docs/*/latest/api/upgrade/:package/:api-*", "destination": "/api/upgrade/:package/:api"},
|
||||
{"type": 301, "source": "/docs/*/latest/api/upgrade/:package/index/:api-*", "destination": "/api/upgrade/:package/:api"},
|
||||
{"type": 301, "source": "/docs/*/latest/glossary", "destination": "/guide/glossary"},
|
||||
{"type": 301, "source": "/docs/*/latest/guide/", "destination": "/docs"},
|
||||
{"type": 301, "source": "/docs/*/latest/guide/lifecycle-hooks", "destination": "/guide/lifecycle-hooks"},
|
||||
{"type": 301, "source": "/docs/*/latest/:rest*", "destination": "/:rest*"},
|
||||
{"type": 301, "source": "/docs/latest/:rest*", "destination": "/:rest*"},
|
||||
{"type": 301, "source": "/docs/styleguide*", "destination": "/guide/styleguide"},
|
||||
{"type": 301, "source": "/guide/metadata", "destination": "/guide/aot-compiler"},
|
||||
{"type": 301, "source": "/guide/ngmodule", "destination": "/guide/ngmodules"},
|
||||
{"type": 301, "source": "/guide/learning-angular*", "destination": "/start"},
|
||||
{"type": 301, "source": "/testing", "destination": "/guide/testing"},
|
||||
{"type": 301, "source": "/testing/**", "destination": "/guide/testing"},
|
||||
|
||||
// Work around Firebase hosting bug with `/index.html?<query>` leading to infinite redirects.
|
||||
// See https://github.com/angular/angular/issues/42518#issuecomment-858545483 for details.
|
||||
{"type": 301, "source": "/index.html:query*", "destination": "/:query*"},
|
||||
|
||||
// Strip off the `.html` extension, because Firebase will not do this automatically any more
|
||||
// (unless the new URL points to an existing file, which is not necessarily the case here).
|
||||
{"type": 301, "source": "/:topLevelFile.html", "destination": "/:topLevelFile"},
|
||||
{"type": 301, "source": "/:somePath*/:file.html", "destination": "/:somePath*/:file"},
|
||||
|
||||
// The below paths are referenced in users projects generated by the CLI
|
||||
{"type": 301, "source": "/config/tsconfig", "destination": "/guide/typescript-configuration"},
|
||||
{"type": 301, "source": "/config/solution-tsconfig", "destination": "https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/#solution-style-tsconfig"},
|
||||
{"type": 301, "source": "/strict", "destination": "/guide/strict-mode"},
|
||||
|
||||
// Use discoverable link for Angular DevTools and redirect to the guide page
|
||||
{"type": 301, "source": "/devtools", "destination": "/guide/devtools"},
|
||||
|
||||
// Use discoverable link for Angular Package Format and redirect to the guide page
|
||||
{"type": 301, "source": "/apf", "destination": "/guide/angular-package-format"},
|
||||
|
||||
// Temporarily serve the guide at `/devtools` as well as `/guide/devtools` until users have
|
||||
// their SW updated to a version that knows about the `/devtools` redirect.
|
||||
{"type": 302, "source": "/generated/docs/devtools.json", "destination": "/generated/docs/guide/devtools.json"},
|
||||
|
||||
// Analytics pages that have been deleted in v15
|
||||
{"type": 301, "source": "/cli/usage-analytics-gathering", "destination": "/cli/analytics"},
|
||||
{"type": 301, "source": "/analytics", "destination": "/cli/analytics"},
|
||||
|
||||
// Deprecated error messages
|
||||
{"type": 301, "source": "/errors/NG6999", "destination": "https://v13.angular.io/errors/NG6999"},
|
||||
|
||||
// Universal has been renamed to ssr
|
||||
{"type": 301, "source": "/guide/universal", "destination": "/guide/ssr"}
|
||||
],
|
||||
"rewrites": [
|
||||
{
|
||||
"source": "**/!(*.*)",
|
||||
"destination": "/index.html"
|
||||
}
|
||||
],
|
||||
"headers": [
|
||||
{
|
||||
// All URLs
|
||||
"source": "**",
|
||||
"headers": [
|
||||
// Report Trusted Types violations
|
||||
{
|
||||
"key": "Content-Security-Policy-Report-Only",
|
||||
|
||||
// The following Trusted Types policies are allowed:
|
||||
// - angular: Angular's main internal policy. Defined in the `@angular/core` package.
|
||||
// - angular#bundler: Used by Angular's bundler. Defined in the `webpack` package, enabled by `@angular-devkit/build-angular`.
|
||||
// - angular#unsafe-bypass: For bypassSecurityTrust* usage. Defined in the `@angular/core` package.
|
||||
// - aio#analytics: For the Legacy Google Analytics snippet. Defined in `index.html`.
|
||||
// - google#safe: Used by the safevalues library. Defined in the `safevalues` package.
|
||||
// - goog#html: Used by the Global Site Tag (`gtag.js`).
|
||||
|
||||
// csp.withgoogle.com is Google's CSP report collecting
|
||||
// infrastructure.
|
||||
// NOTE: Keep in sync with the header in `angular.json`.
|
||||
"value": "require-trusted-types-for 'script'; trusted-types angular angular#bundler angular#unsafe-bypass aio#analytics google#safe goog#html; report-uri https://csp.withgoogle.com/csp/angular.io"
|
||||
},
|
||||
{
|
||||
"key": "X-Frame-Options",
|
||||
"value": "DENY"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
// All paths (URLs without a file extension).
|
||||
"source": "**/!(*.*)",
|
||||
"headers": [
|
||||
{"key": "Cache-Control", "value": "no-cache"},
|
||||
{"key": "Link", "value": "</generated/navigation.json>;rel=preload;as=fetch,</generated/docs/index.json>;rel=preload;as=fetch"}
|
||||
]
|
||||
},
|
||||
{
|
||||
// Images, fonts, (non-hashed) CSS/JS files.
|
||||
"source": "**/*.@(gif|jpg|jpeg|png|svg|webp|js|css|eot|otf|ttf|ttc|woff|woff2)",
|
||||
"headers": [
|
||||
{"key": "Cache-Control", "value": "max-age=86400"} // 1 day
|
||||
]
|
||||
},
|
||||
{
|
||||
// Hashed CSS/JS files...
|
||||
"source": "**/*.+([0-9a-f]).@(css|js)",
|
||||
"headers": [
|
||||
{"key": "Cache-Control", "value": "max-age=2592000"} // 30 days
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"database": {
|
||||
"rules": "database.rules.json"
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
const {getAdjustedChromeBinPathForWindows} = require('./tools/windows-chromium-path');
|
||||
|
||||
process.env.CHROME_BIN = getAdjustedChromeBinPathForWindows();
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage'),
|
||||
require('@angular-devkit/build-angular/plugins/karma'),
|
||||
{'reporter:jasmine-seed': ['type', JasmineSeedReporter]},
|
||||
],
|
||||
proxies: {
|
||||
'/dummy/image': 'src/assets/images/logos/angular/angular.png',
|
||||
},
|
||||
client: {
|
||||
clearContext: false, // leave Jasmine Spec Runner output visible in browser
|
||||
jasmine: {
|
||||
// you can add configuration options for Jasmine here
|
||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||
// for example, you can disable the random execution with `random: false`
|
||||
// or set a specific seed with `seed: 4321`
|
||||
random: true,
|
||||
seed: '',
|
||||
},
|
||||
},
|
||||
jasmineHtmlReporter: {
|
||||
suppressAll: true // removes the duplicated traces
|
||||
},
|
||||
coverageReporter: {
|
||||
dir: require('path').join(__dirname, './coverage/site'),
|
||||
subdir: '.',
|
||||
reporters: [
|
||||
{ type: 'html' },
|
||||
{ type: 'text-summary' }
|
||||
],
|
||||
},
|
||||
reporters: ['progress', 'kjhtml', 'jasmine-seed'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
customLaunchers: {
|
||||
ChromeHeadlessNoSandbox: {
|
||||
base: 'ChromeHeadless',
|
||||
// See /integration/README.md#browser-tests for more info on these args
|
||||
flags: ['--no-sandbox', '--headless', '--disable-gpu', '--disable-dev-shm-usage', '--hide-scrollbars', '--mute-audio'],
|
||||
},
|
||||
},
|
||||
browsers: ['ChromeHeadlessNoSandbox'],
|
||||
browserNoActivityTimeout: 60000,
|
||||
singleRun: false,
|
||||
restartOnFileChange: true,
|
||||
});
|
||||
};
|
||||
|
||||
// Helpers
|
||||
function JasmineSeedReporter(baseReporterDecorator) {
|
||||
baseReporterDecorator(this);
|
||||
|
||||
this.onBrowserComplete = (browser, result) => {
|
||||
const seed = result.order && result.order.random && result.order.seed;
|
||||
if (seed) this.write(`${browser}: Randomized with seed ${seed}.\n`);
|
||||
};
|
||||
|
||||
this.onRunComplete = () => undefined;
|
||||
}
|
||||