Compare commits
47 Commits
Author | SHA1 | Date | |
---|---|---|---|
8bc2f0647b | |||
b71ccc2954 | |||
2daa838a04 | |||
a2716acf4c | |||
7a0cc534df | |||
5f52e63857 | |||
9409dce93c | |||
2fa788c9e6 | |||
73e667f61f | |||
a7d5d33f0a | |||
7e511e78cd | |||
9e76a38073 | |||
242981963e | |||
970df9ebaf | |||
4c7e7fbd09 | |||
5f78456170 | |||
36fd063737 | |||
c1b7f0370e | |||
882a9e3856 | |||
362b3e4d03 | |||
2952ea57a5 | |||
3541e590f4 | |||
8ef0ae3561 | |||
c3ff66c1ba | |||
a1d9848456 | |||
a3482f7076 | |||
006af0b985 | |||
9dc4815e39 | |||
4263d9ea0d | |||
30253a7df3 | |||
16b83e8e2f | |||
e0c10632ea | |||
686b62129c | |||
dd2587d9e5 | |||
2742649a52 | |||
eb0461d2d4 | |||
e24393c35b | |||
8237e958a6 | |||
9ba898d588 | |||
cd1b0c1b1f | |||
06072e0062 | |||
288e0ef7b6 | |||
88ad5052bf | |||
7e6644a25a | |||
d533d15001 | |||
d0abf1bc54 | |||
56ac18ea8c |
@ -7,158 +7,162 @@
|
||||
# To validate changes, use an online parser, eg.
|
||||
# http://yaml-online-parser.appspot.com/
|
||||
|
||||
# Note that the browser docker image comes with Chrome and Firefox preinstalled. This is just
|
||||
# needed for jobs that run tests without Bazel. Bazel runs tests with browsers that will be
|
||||
# fetched by the Webtesting rules. Therefore for jobs that run tests with Bazel, we don't need a
|
||||
# docker image with browsers pre-installed.
|
||||
# **NOTE 1**: Pin to exact images using an ID (SHA). See https://circleci.com/docs/2.0/circleci-images/#using-a-docker-image-id-to-pin-an-image-to-a-fixed-version.
|
||||
# (Using the tag in not necessary when pinning by ID, but include it anyway for documentation purposes.)
|
||||
# **NOTE 2**: If you change the version of the docker images, also change the `cache_key` suffix.
|
||||
# **NOTE 3**: If you change the version of the `*-browsers` docker image, make sure the
|
||||
# `CI_CHROMEDRIVER_VERSION_ARG` env var (in `.circleci/env.sh`) points to a ChromeDriver
|
||||
# version that is compatible with the Chrome version in the image.
|
||||
var_1: &default_docker_image circleci/node:10.16@sha256:75c05084fff4afa3683a03c5a04a4a3ad95c536ff2439d8fe14e7e1f5c58b09a
|
||||
var_2: &browsers_docker_image circleci/node:10.16-browsers@sha256:d2a96fe1cbef51257ee626b5f645e64dade3e886f00ba9cb7e8ea65b4efe8db1
|
||||
# CircleCI configuration version
|
||||
# Version 2.1 allows for extra config reuse features
|
||||
# https://circleci.com/docs/2.0/reusing-config/#getting-started-with-config-reuse
|
||||
version: 2.1
|
||||
|
||||
# We don't want to include the current branch name in the cache key because that would prevent
|
||||
# PRs from being able to restore the cache since the branch names are always different for PRs.
|
||||
# The cache key should only consist of dynamic values that change whenever something in the
|
||||
# cache changes. For example:
|
||||
# 1) yarn lock file changes --> cached "node_modules" are different.
|
||||
# 2) bazel repository definitions change --> cached bazel repositories are different.
|
||||
# **NOTE 1 **: If you change the cache key prefix, also sync the restore_cache fallback to match.
|
||||
# **NOTE 1 **: If you change the cache key prefix, also sync the cache_key_fallback to match.
|
||||
# **NOTE 2 **: Keep the static part of the cache key as prefix to enable correct fallbacks.
|
||||
# See https://circleci.com/docs/2.0/caching/#restoring-cache for how prefixes work in CircleCI.
|
||||
var_3: &cache_key v3-angular-node-10.16-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
||||
|
||||
# Initializes the CI environment by setting up common environment variables.
|
||||
var_4: &init_environment
|
||||
run:
|
||||
name: Initializing environment (setting up variables, overwriting Yarn)
|
||||
# Overwrite the yarn installed in the docker container with our own version.
|
||||
command: |
|
||||
./.circleci/env.sh
|
||||
ourYarn=$(realpath ./third_party/github.com/yarnpkg/yarn/releases/download/v1.17.3/bin/yarn.js)
|
||||
sudo chmod a+x $ourYarn
|
||||
sudo ln -fs $ourYarn /usr/local/bin/yarn
|
||||
echo "Yarn version: $(yarn --version)"
|
||||
|
||||
# Add GitHub to known hosts.
|
||||
mkdir -p ~/.ssh
|
||||
echo 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==' >> ~/.ssh/known_hosts
|
||||
|
||||
# use git+ssh instead of https
|
||||
git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true
|
||||
git config --global gc.auto 0 || true
|
||||
|
||||
|
||||
var_5: &setup_bazel_remote_execution
|
||||
run:
|
||||
name: "Setup bazel RBE remote execution"
|
||||
command: |
|
||||
# We need ensure that the same default digest is used for encoding and decoding
|
||||
# with openssl. Openssl versions might have different default digests which can
|
||||
# cause decryption failures based on the openssl version. https://stackoverflow.com/a/39641378/4317734
|
||||
openssl aes-256-cbc -d -in .circleci/gcp_token -md md5 -k "$CI_REPO_NAME" -out /home/circleci/.gcp_credentials
|
||||
echo "export GOOGLE_APPLICATION_CREDENTIALS=/home/circleci/.gcp_credentials" >> $BASH_ENV
|
||||
./.circleci/setup-rbe.sh .bazelrc.user
|
||||
|
||||
# Settings common to each job
|
||||
var_6: &job_defaults
|
||||
working_directory: ~/ng
|
||||
docker:
|
||||
- image: *default_docker_image
|
||||
|
||||
# After checkout, rebase on top of target branch.
|
||||
var_7: &post_checkout
|
||||
run:
|
||||
name: Rebase PR on target branch
|
||||
command: >
|
||||
if [[ -n "${CIRCLE_PR_NUMBER}" ]]; then
|
||||
# User is required for rebase.
|
||||
git config user.name "angular-ci"
|
||||
git config user.email "angular-ci"
|
||||
# Rebase PR on top of target branch.
|
||||
node tools/rebase-pr.js angular/angular ${CIRCLE_PR_NUMBER}
|
||||
else
|
||||
echo "This build is not over a PR, nothing to do."
|
||||
fi
|
||||
|
||||
var_8: &yarn_install
|
||||
run:
|
||||
name: Running Yarn install
|
||||
command: |
|
||||
# Yarn's requests sometimes take more than 10mins to complete.
|
||||
# Print something to stdout, to prevent CircleCI from failing due to not output.
|
||||
while true; do sleep 60; echo "[`date`] Keeping alive..."; done &
|
||||
KEEP_ALIVE_PID=$!
|
||||
yarn install --frozen-lockfile --non-interactive
|
||||
kill $KEEP_ALIVE_PID
|
||||
|
||||
var_9: &setup_circleci_bazel_config
|
||||
run:
|
||||
name: Setting up CircleCI bazel configuration
|
||||
command: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
||||
|
||||
var_10: &restore_cache
|
||||
restore_cache:
|
||||
keys:
|
||||
- *cache_key
|
||||
# This fallback should be the cache_key without variables.
|
||||
- v3-angular-node-10.16-
|
||||
|
||||
# Branch filter that can be specified for jobs that should only run on publish branches
|
||||
# (e.g. master or the patch branch)
|
||||
var_11: &publish_branches_filter
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
# e.g. 7.0.x, 7.1.x, etc.
|
||||
- /\d+\.\d+\.x/
|
||||
|
||||
# Workspace initially persisted by the `install` job, and then enhanced by `test_aio` and
|
||||
# `build-npm-packages`.
|
||||
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
|
||||
# https://circleci.com/blog/deep-diving-into-circleci-workspaces/
|
||||
var_12: &attach_workspace
|
||||
attach_workspace:
|
||||
at: ~/
|
||||
|
||||
var_13: ¬ify_caretaker_on_fail
|
||||
run:
|
||||
when: on_fail
|
||||
name: Notify caretaker about failure
|
||||
# `$SLACK_CARETAKER_WEBHOOK_URL` is a secret env var defined in CircleCI project settings.
|
||||
# The URL comes from https://angular-team.slack.com/apps/A0F7VRE7N-circleci.
|
||||
command: |
|
||||
notificationJson="{\"text\":\":x: \`$CIRCLE_JOB\` job for $CIRCLE_BRANCH branch failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}"
|
||||
curl --request POST --header "Content-Type: application/json" --data "$notificationJson" $SLACK_CARETAKER_WEBHOOK_URL
|
||||
|
||||
var_14: ¬ify_dev_infra_on_fail
|
||||
run:
|
||||
when: on_fail
|
||||
name: Notify dev-infra about failure
|
||||
# `$SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL` is a secret env var defined in CircleCI project settings.
|
||||
# The URL comes from https://angular-team.slack.com/apps/A0F7VRE7N-circleci.
|
||||
command: |
|
||||
notificationJson="{\"text\":\":x: \`$CIRCLE_JOB\` job for $CIRCLE_BRANCH branch failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}"
|
||||
curl --request POST --header "Content-Type: application/json" --data "$notificationJson" $SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
||||
var_4: &cache_key_fallback v3-angular-node-10.16-
|
||||
|
||||
# Cache key for the Material unit tests job. **Note** when updating the SHA in the cache keys,
|
||||
# also update the SHA for the "MATERIAL_REPO_COMMIT" environment variable.
|
||||
var_15: &material_unit_tests_cache_key v4-angular-material-18b9ef3f5529f0fa8f034944681486447af7b879
|
||||
var_16: &material_unit_tests_cache_key_short v4-angular-material
|
||||
var_5: &material_unit_tests_cache_key v4-angular-material-18b9ef3f5529f0fa8f034944681486447af7b879
|
||||
var_6: &material_unit_tests_cache_key_fallback v4-angular-material-
|
||||
|
||||
version: 2
|
||||
|
||||
# Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages` and
|
||||
# `build-ivy-npm-packages`.
|
||||
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
|
||||
# https://circleci.com/blog/deep-diving-into-circleci-workspaces/
|
||||
var_7: &workspace_location ~/
|
||||
|
||||
# Executor Definitions
|
||||
# https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-executors
|
||||
# **NOTE 1**: Pin to exact images using an ID (SHA). See https://circleci.com/docs/2.0/circleci-images/#using-a-docker-image-id-to-pin-an-image-to-a-fixed-version.
|
||||
# (Using the tag in not necessary when pinning by ID, but include it anyway for documentation purposes.)
|
||||
# **NOTE 2**: If you change the version of the docker images, also change the `cache_key` suffix.
|
||||
# **NOTE 3**: If you change the version of the `*-browsers` docker image, make sure the
|
||||
# `CI_CHROMEDRIVER_VERSION_ARG` env var (in `.circleci/env.sh`) points to a ChromeDriver
|
||||
# version that is compatible with the Chrome version in the image.
|
||||
executors:
|
||||
default-executor:
|
||||
parameters:
|
||||
resource_class:
|
||||
type: string
|
||||
default: medium
|
||||
docker:
|
||||
- image: circleci/node:10.16@sha256:75c05084fff4afa3683a03c5a04a4a3ad95c536ff2439d8fe14e7e1f5c58b09a
|
||||
resource_class: << parameters.resource_class >>
|
||||
working_directory: ~/ng
|
||||
|
||||
browsers-executor:
|
||||
parameters:
|
||||
resource_class:
|
||||
type: string
|
||||
default: medium
|
||||
docker:
|
||||
# The browser docker image comes with Chrome and Firefox preinstalled. This is just
|
||||
# needed for jobs that run tests without Bazel. Bazel runs tests with browsers that will be
|
||||
# fetched by the Webtesting rules. Therefore for jobs that run tests with Bazel, we don't need a
|
||||
# docker image with browsers pre-installed.
|
||||
- image: circleci/node:10.16-browsers@sha256:d2a96fe1cbef51257ee626b5f645e64dade3e886f00ba9cb7e8ea65b4efe8db1
|
||||
resource_class: << parameters.resource_class >>
|
||||
working_directory: ~/ng
|
||||
|
||||
# Command Definitions
|
||||
# https://circleci.com/docs/2.0/reusing-config/#authoring-reusable-commands
|
||||
commands:
|
||||
custom_attach_workspace:
|
||||
description: Attach workspace at a predefined location
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: *workspace_location
|
||||
|
||||
# Initializes the CI environment by setting up common environment variables.
|
||||
init_environment:
|
||||
description: Initializing environment (setting up variables, overwriting Yarn)
|
||||
steps:
|
||||
- run: ./.circleci/env.sh
|
||||
- run:
|
||||
# Overwrite the yarn installed in the docker container with our own version.
|
||||
name: Overwrite yarn with our own version
|
||||
command: |
|
||||
ourYarn=$(realpath ./third_party/github.com/yarnpkg/yarn/releases/download/v1.17.3/bin/yarn.js)
|
||||
sudo chmod a+x $ourYarn
|
||||
sudo ln -fs $ourYarn /usr/local/bin/yarn
|
||||
- run: echo "Yarn version $(yarn --version)"
|
||||
- run:
|
||||
# Configure git as the CircleCI `checkout` command does.
|
||||
# This is needed because we only checkout on the setup job.
|
||||
# Add GitHub to known hosts
|
||||
name: Configure git
|
||||
command: |
|
||||
mkdir -p ~/.ssh
|
||||
echo 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==' >> ~/.ssh/known_hosts
|
||||
git config --global url."ssh://git@github.com".insteadOf "https://github.com" || true
|
||||
git config --global gc.auto 0 || true
|
||||
|
||||
setup_circleci_bazel_config:
|
||||
description: Set up CircleCI bazel configuration
|
||||
steps:
|
||||
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
||||
|
||||
setup_bazel_rbe:
|
||||
description: Setup bazel RBE remote execution
|
||||
steps:
|
||||
# We need ensure that the same default digest is used for encoding and decoding
|
||||
# with openssl. Openssl versions might have different default digests which can
|
||||
# cause decryption failures based on the openssl version. https://stackoverflow.com/a/39641378/4317734
|
||||
- run: openssl aes-256-cbc -d -in .circleci/gcp_token -md md5 -k "$CI_REPO_NAME" -out /home/circleci/.gcp_credentials
|
||||
- run: echo "export GOOGLE_APPLICATION_CREDENTIALS=/home/circleci/.gcp_credentials" >> $BASH_ENV
|
||||
- run: ./.circleci/setup-rbe.sh .bazelrc.user
|
||||
|
||||
notify_webhook_on_fail:
|
||||
description: Notify a webhook about failure
|
||||
parameters:
|
||||
# `webhook_url_env_var` are secret env vars defined in CircleCI project settings.
|
||||
# The URLs come from https://angular-team.slack.com/apps/A0F7VRE7N-circleci.
|
||||
webhook_url_env_var:
|
||||
type: env_var_name
|
||||
steps:
|
||||
- run:
|
||||
when: on_fail
|
||||
command: |
|
||||
notificationJson="{\"text\":\":x: \`$CIRCLE_JOB\` job for $CIRCLE_BRANCH branch failed on build $CIRCLE_BUILD_NUM: $CIRCLE_BUILD_URL :scream:\"}"
|
||||
curl --request POST --header "Content-Type: application/json" --data "$notificationJson" ${<< parameters.webhook_url_env_var >>}
|
||||
|
||||
# Job definitions
|
||||
# Jobs can include parameters that are passed in the workflow job invocation.
|
||||
# https://circleci.com/docs/2.0/reusing-config/#authoring-parameterized-jobs
|
||||
jobs:
|
||||
setup:
|
||||
<<: *job_defaults
|
||||
executor: default-executor
|
||||
steps:
|
||||
- checkout
|
||||
- *post_checkout
|
||||
- run:
|
||||
name: Rebase PR on target branch
|
||||
# After checkout, rebase on top of target branch.
|
||||
command: >
|
||||
if [[ -n "${CIRCLE_PR_NUMBER}" ]]; then
|
||||
# User is required for rebase.
|
||||
git config user.name "angular-ci"
|
||||
git config user.email "angular-ci"
|
||||
# Rebase PR on top of target branch.
|
||||
node tools/rebase-pr.js angular/angular ${CIRCLE_PR_NUMBER}
|
||||
else
|
||||
echo "This build is not over a PR, nothing to do."
|
||||
fi
|
||||
# This cache is saved in the build-npm-packages so that Bazel cache is also included.
|
||||
- *restore_cache
|
||||
- *init_environment
|
||||
- *yarn_install
|
||||
- restore_cache:
|
||||
keys:
|
||||
- *cache_key
|
||||
- *cache_key_fallback
|
||||
- init_environment
|
||||
- run:
|
||||
name: Running Yarn install
|
||||
command: yarn install --frozen-lockfile --non-interactive
|
||||
# Yarn's requests sometimes take more than 10mins to complete.
|
||||
no_output_timeout: 45m
|
||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
||||
# Make the bazel directories and add a file to them if they don't exist already so that
|
||||
# persist_to_workspace does not fail.
|
||||
@ -168,19 +172,18 @@ jobs:
|
||||
touch ~/bazel_repository_cache/MARKER
|
||||
fi
|
||||
# Persist any changes at this point to be reused by further jobs.
|
||||
# **NOTE 1 **: Folders persisted here should be kept in sync with `var_13: &attach_workspace`.
|
||||
# **NOTE 2 **: To add new content to the workspace, always persist on the same root.
|
||||
# **NOTE**: To add new content to the workspace, always persist on the same root.
|
||||
- persist_to_workspace:
|
||||
root: ~/
|
||||
root: *workspace_location
|
||||
paths:
|
||||
- ./ng
|
||||
- ./bazel_repository_cache
|
||||
|
||||
lint:
|
||||
<<: *job_defaults
|
||||
executor: default-executor
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
|
||||
- run: 'yarn bazel:format -mode=check ||
|
||||
(echo "BUILD files not formatted. Please run ''yarn bazel:format''" ; exit 1)'
|
||||
@ -192,25 +195,29 @@ jobs:
|
||||
- run: node tools/verify-codeownership
|
||||
|
||||
test:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
executor:
|
||||
name: default-executor
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- *setup_circleci_bazel_config
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- setup_circleci_bazel_config
|
||||
# Setup remote execution and run RBE-compatible tests.
|
||||
- *setup_bazel_remote_execution
|
||||
- run: yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only
|
||||
- setup_bazel_rbe
|
||||
- run:
|
||||
command: yarn bazel test //... --build_tag_filters=-ivy-only --test_tag_filters=-ivy-only
|
||||
no_output_timeout: 20m
|
||||
|
||||
# Temporary job to test what will happen when we flip the Ivy flag to true
|
||||
test_ivy_aot:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
executor:
|
||||
name: default-executor
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- *setup_circleci_bazel_config
|
||||
- *setup_bazel_remote_execution
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- setup_circleci_bazel_config
|
||||
- setup_bazel_rbe
|
||||
|
||||
# We need to explicitly specify the --symlink_prefix option because otherwise we would
|
||||
# not be able to easily find the output bin directory when uploading artifacts for size
|
||||
@ -242,16 +249,17 @@ jobs:
|
||||
#
|
||||
# NOTE: This is currently limited to master builds only. See the `default_workflow` configuration.
|
||||
test_saucelabs_bazel:
|
||||
<<: *job_defaults
|
||||
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
|
||||
# container for this job. This is necessary because we launch a lot of browsers concurrently
|
||||
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
|
||||
resource_class: xlarge
|
||||
executor:
|
||||
name: default-executor
|
||||
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
|
||||
# container for this job. This is necessary because we launch a lot of browsers concurrently
|
||||
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- *setup_circleci_bazel_config
|
||||
- *setup_bazel_remote_execution
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- setup_circleci_bazel_config
|
||||
- setup_bazel_rbe
|
||||
- run:
|
||||
name: Run Bazel tests in saucelabs
|
||||
# All web tests are contained within a single //:test_web_all target for Saucelabs
|
||||
@ -266,16 +274,16 @@ jobs:
|
||||
--username $SAUCE_USERNAME \
|
||||
--key $(echo $SAUCE_ACCESS_KEY | rev) \
|
||||
yarn bazel test //:test_web_all
|
||||
- *notify_dev_infra_on_fail
|
||||
no_output_timeout: 20m
|
||||
- notify_webhook_on_fail:
|
||||
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
||||
|
||||
test_aio:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
|
||||
- image: *browsers_docker_image
|
||||
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
|
||||
executor: browsers-executor
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
# Build aio
|
||||
- run: yarn --cwd aio build --progress=false
|
||||
# Lint the code
|
||||
@ -294,27 +302,27 @@ jobs:
|
||||
- run: yarn --cwd aio redirects-test
|
||||
|
||||
deploy_aio:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
# Needed because before deploying the deploy-production script runs the PWA score tests.
|
||||
- image: *browsers_docker_image
|
||||
executor: browsers-executor
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
# Deploy angular.io to production (if necessary)
|
||||
- run: setPublicVar_CI_STABLE_BRANCH
|
||||
- run: yarn --cwd aio deploy-production
|
||||
|
||||
test_aio_local:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
|
||||
- image: *browsers_docker_image
|
||||
parameters:
|
||||
ivy:
|
||||
type: boolean
|
||||
default: false
|
||||
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
|
||||
executor: browsers-executor
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
# Build aio (with local Angular packages)
|
||||
- run: yarn --cwd aio build-local-ci
|
||||
- run: yarn --cwd aio build-local<<# parameters.ivy >>-with-ivy<</ parameters.ivy >>-ci
|
||||
# Run unit tests
|
||||
- run: yarn --cwd aio test --progress=false --watch=false
|
||||
# Run e2e tests
|
||||
@ -322,32 +330,13 @@ jobs:
|
||||
# Run PWA-score tests
|
||||
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
|
||||
# Check the bundle sizes.
|
||||
- run: yarn --cwd aio payload-size aio-local
|
||||
|
||||
test_aio_local_ivy:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
# Needed because the AIO tests and the PWA score test depend on Chrome being available.
|
||||
- image: *browsers_docker_image
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
# Build aio with Ivy (using local Angular packages)
|
||||
- run: yarn --cwd aio build-with-ivy-ci
|
||||
# Run unit tests
|
||||
- run: yarn --cwd aio test --progress=false --watch=false
|
||||
# Run e2e tests
|
||||
- run: yarn --cwd aio e2e --configuration=ci
|
||||
# Run PWA-score tests
|
||||
- run: yarn --cwd aio test-pwa-score-localhost $CI_AIO_MIN_PWA_SCORE
|
||||
# Check the bundle sizes.
|
||||
- run: yarn --cwd aio payload-size aio-local-ivy
|
||||
- run: yarn --cwd aio payload-size aio-local<<# parameters.ivy >>-ivy<</ parameters.ivy >>
|
||||
|
||||
test_aio_tools:
|
||||
<<: *job_defaults
|
||||
executor: default-executor
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
# Install
|
||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
||||
- run: yarn --cwd aio extract-cli-command-docs
|
||||
@ -356,56 +345,42 @@ jobs:
|
||||
- run: ./aio/aio-builds-setup/scripts/test.sh
|
||||
|
||||
test_docs_examples:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
parameters:
|
||||
ivy:
|
||||
type: boolean
|
||||
default: false
|
||||
executor:
|
||||
# Needed because the example e2e tests depend on Chrome.
|
||||
- image: *browsers_docker_image
|
||||
parallelism: 4
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
# Install aio
|
||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
||||
# Run examples tests. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled.
|
||||
# Since the parallelism is set to "3", there will be three parallel CircleCI containers
|
||||
# with either "0", "1" or "2" as node index. This can be passed to the "--shard" argument.
|
||||
- run: yarn --cwd aio example-e2e --setup --local --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} --retry 2
|
||||
|
||||
test_docs_examples_ivy:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
# Needed because the example e2e tests depend on Chrome.
|
||||
- image: *browsers_docker_image
|
||||
resource_class: xlarge
|
||||
# We increase the parallelism here to five while the "test_docs_examples" job runs with
|
||||
# a parallelism of four. This is necessary because this job also need to run NGCC which
|
||||
# takes up more time and we don't want these jobs to impact the overall CI turnaround.
|
||||
name: browsers-executor
|
||||
resource_class: xlarge
|
||||
parallelism: 5
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
# Install aio
|
||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
||||
# Rename the Ivy packages dist folder to "dist/packages-dist" as the AIO
|
||||
# package installer picks up the locally built packages from that location.
|
||||
# *Note*: We could also adjust the packages installer, but given we won't have
|
||||
# two different folders of Angular distributions in the future, we should keep
|
||||
# the packages installer unchanged.
|
||||
- run: mv dist/packages-dist-ivy-aot dist/packages-dist
|
||||
# Run examples tests with ivy. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled.
|
||||
# Since the parallelism is set to "3", there will be three parallel CircleCI containers
|
||||
# with either "0", "1" or "2" as node index. This can be passed to the "--shard" argument.
|
||||
- run: yarn --cwd aio example-e2e --setup --local --ivy --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} --retry 2
|
||||
- when:
|
||||
condition: << parameters.ivy >>
|
||||
steps:
|
||||
# Rename the Ivy packages dist folder to "dist/packages-dist" as the AIO
|
||||
# package installer picks up the locally built packages from that location.
|
||||
# *Note*: We could also adjust the packages installer, but given we won't have
|
||||
# two different folders of Angular distributions in the future, we should keep
|
||||
# the packages installer unchanged.
|
||||
- run: mv dist/packages-dist-ivy-aot dist/packages-dist
|
||||
# Run examples tests. The "CIRCLE_NODE_INDEX" will be set if "parallelism" is enabled.
|
||||
# Since the parallelism is set to "5", there will be five parallel CircleCI containers.
|
||||
# with either "0", "1", etc as node index. This can be passed to the "--shard" argument.
|
||||
- run: yarn --cwd aio example-e2e --setup --local <<# parameters.ivy >>--ivy<</ parameters.ivy >> --cliSpecsConcurrency=5 --shard=${CIRCLE_NODE_INDEX}/${CIRCLE_NODE_TOTAL} --retry 2
|
||||
|
||||
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
||||
aio_preview:
|
||||
<<: *job_defaults
|
||||
executor: default-executor
|
||||
environment:
|
||||
AIO_SNAPSHOT_ARTIFACT_PATH: &aio_preview_artifact_path 'aio/tmp/snapshot.tgz'
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- run: ./aio/scripts/build-artifacts.sh $AIO_SNAPSHOT_ARTIFACT_PATH $CI_PULL_REQUEST $CI_COMMIT
|
||||
- store_artifacts:
|
||||
path: *aio_preview_artifact_path
|
||||
@ -416,13 +391,11 @@ jobs:
|
||||
|
||||
# This job should only be run on PR builds, where `CI_PULL_REQUEST` is not `false`.
|
||||
test_aio_preview:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
# Needed because the test-preview script runs e2e tests and the PWA score test with Chrome.
|
||||
- image: *browsers_docker_image
|
||||
# Needed because the test-preview script runs e2e tests and the PWA score test with Chrome.
|
||||
executor: browsers-executor
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- run: yarn --cwd aio install --frozen-lockfile --non-interactive
|
||||
- run:
|
||||
name: Wait for preview and run tests
|
||||
@ -438,19 +411,20 @@ jobs:
|
||||
|
||||
# Build the view engine npm packages. No new jobs should depend on this.
|
||||
build-npm-packages:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
executor:
|
||||
name: default-executor
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- *setup_circleci_bazel_config
|
||||
- *setup_bazel_remote_execution
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- setup_circleci_bazel_config
|
||||
- setup_bazel_rbe
|
||||
|
||||
- run: scripts/build-packages-dist.sh
|
||||
|
||||
# Save the npm packages from //packages/... for other workflow jobs to read
|
||||
- persist_to_workspace:
|
||||
root: ~/
|
||||
root: *workspace_location
|
||||
paths:
|
||||
- ng/dist/packages-dist
|
||||
|
||||
@ -464,19 +438,20 @@ jobs:
|
||||
|
||||
# Build the ivy npm packages.
|
||||
build-ivy-npm-packages:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
executor:
|
||||
name: default-executor
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- *setup_circleci_bazel_config
|
||||
- *setup_bazel_remote_execution
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- setup_circleci_bazel_config
|
||||
- setup_bazel_rbe
|
||||
|
||||
- run: scripts/build-ivy-npm-packages.sh
|
||||
|
||||
# Save the npm packages from //packages/... for other workflow jobs to read
|
||||
- persist_to_workspace:
|
||||
root: ~/
|
||||
root: *workspace_location
|
||||
paths:
|
||||
- ng/dist/packages-dist-ivy-aot
|
||||
|
||||
@ -487,18 +462,17 @@ jobs:
|
||||
# need to re-run manually should be alleviated.
|
||||
# See comments inside the integration/run_tests.sh script.
|
||||
integration_test:
|
||||
<<: *job_defaults
|
||||
parallelism: 4
|
||||
docker:
|
||||
executor:
|
||||
# Needed because the integration tests expect Chrome to be installed (e.g cli-hello-world)
|
||||
- image: *browsers_docker_image
|
||||
# Note: we run Bazel in one of the integration tests, and it can consume >2G
|
||||
# of memory. Together with the system under test, this can exhaust the RAM
|
||||
# on a 4G worker so we use a larger machine here too.
|
||||
resource_class: xlarge
|
||||
name: browsers-executor
|
||||
# Note: we run Bazel in one of the integration tests, and it can consume >2G
|
||||
# of memory. Together with the system under test, this can exhaust the RAM
|
||||
# on a 4G worker so we use a larger machine here too.
|
||||
resource_class: xlarge
|
||||
parallelism: 4
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
# Runs the integration tests in parallel across multiple CircleCI container instances. The
|
||||
# amount of container nodes for this job is controlled by the "parallelism" option.
|
||||
- run: ./integration/run_tests.sh ${CIRCLE_NODE_INDEX} ${CIRCLE_NODE_TOTAL}
|
||||
@ -506,7 +480,7 @@ jobs:
|
||||
# This job updates the content of repos like github.com/angular/core-builds
|
||||
# for every green build on angular/angular.
|
||||
publish_snapshot:
|
||||
<<: *job_defaults
|
||||
executor: default-executor
|
||||
steps:
|
||||
# See below - ideally this job should not trigger for non-upstream builds.
|
||||
# But since it does, we have to check this condition.
|
||||
@ -520,8 +494,8 @@ jobs:
|
||||
[[ "$CIRCLE_PROJECT_REPONAME" != "angular" ]]; then
|
||||
circleci step halt
|
||||
fi
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
# CircleCI has a config setting to force SSH for all github connections
|
||||
# This is not compatible with our mechanism of using a Personal Access Token
|
||||
# Clear the global setting
|
||||
@ -535,14 +509,12 @@ jobs:
|
||||
- run: ./scripts/ci/publish-build-artifacts.sh
|
||||
|
||||
aio_monitoring_stable:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
# This job needs Chrome to be globally installed because the tests run with Protractor
|
||||
# which does not load the browser through the Bazel webtesting rules.
|
||||
- image: *browsers_docker_image
|
||||
# This job needs Chrome to be globally installed because the tests run with Protractor
|
||||
# which does not load the browser through the Bazel webtesting rules.
|
||||
executor: browsers-executor
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- run: setPublicVar_CI_STABLE_BRANCH
|
||||
- run:
|
||||
name: Check out `aio/` from the stable branch
|
||||
@ -552,33 +524,36 @@ jobs:
|
||||
- run:
|
||||
name: Run tests against https://angular.io/
|
||||
command: ./aio/scripts/test-production.sh https://angular.io/ $CI_AIO_MIN_PWA_SCORE
|
||||
- *notify_caretaker_on_fail
|
||||
- *notify_dev_infra_on_fail
|
||||
- notify_webhook_on_fail:
|
||||
webhook_url_env_var: SLACK_CARETAKER_WEBHOOK_URL
|
||||
- notify_webhook_on_fail:
|
||||
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
||||
|
||||
aio_monitoring_next:
|
||||
<<: *job_defaults
|
||||
docker:
|
||||
# This job needs Chrome to be globally installed because the tests run with Protractor
|
||||
# which does not load the browser through the Bazel webtesting rules.
|
||||
- image: *browsers_docker_image
|
||||
# This job needs Chrome to be globally installed because the tests run with Protractor
|
||||
# which does not load the browser through the Bazel webtesting rules.
|
||||
executor: browsers-executor
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- run:
|
||||
name: Run tests against https://next.angular.io/
|
||||
command: ./aio/scripts/test-production.sh https://next.angular.io/ $CI_AIO_MIN_PWA_SCORE
|
||||
- *notify_caretaker_on_fail
|
||||
- *notify_dev_infra_on_fail
|
||||
- notify_webhook_on_fail:
|
||||
webhook_url_env_var: SLACK_CARETAKER_WEBHOOK_URL
|
||||
- notify_webhook_on_fail:
|
||||
webhook_url_env_var: SLACK_DEV_INFRA_CI_FAILURES_WEBHOOK_URL
|
||||
|
||||
legacy-unit-tests-saucelabs:
|
||||
<<: *job_defaults
|
||||
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
|
||||
# container for this job. This is necessary because we launch a lot of browsers concurrently
|
||||
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
|
||||
resource_class: xlarge
|
||||
executor:
|
||||
name: default-executor
|
||||
# In order to avoid the bottleneck of having a slow host machine, we acquire a better
|
||||
# container for this job. This is necessary because we launch a lot of browsers concurrently
|
||||
# and therefore the tunnel and Karma need to process a lot of file requests and tests.
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- run:
|
||||
name: Preparing environment for running tests on Saucelabs.
|
||||
command: |
|
||||
@ -597,10 +572,10 @@ jobs:
|
||||
- run: ./scripts/saucelabs/stop-tunnel.sh
|
||||
|
||||
legacy-misc-tests:
|
||||
<<: *job_defaults
|
||||
executor: default-executor
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
- run: yarn gulp check-cycle
|
||||
# TODO: disabled because the Bazel packages-dist does not seem to have map files for
|
||||
# the ESM5/ES2015 output. See: https://github.com/angular/angular/issues/27966
|
||||
@ -609,23 +584,22 @@ jobs:
|
||||
# Job to run unit tests from angular/material2. Needs a browser since all
|
||||
# component unit tests assume they're running in the browser environment.
|
||||
material-unit-tests:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
docker:
|
||||
- image: *browsers_docker_image
|
||||
executor:
|
||||
name: browsers-executor
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
# Although RBE is configured below for the Material repo, also setup RBE in the Angular repo
|
||||
# to provision Angular's GCP token into the environment variables.
|
||||
- *setup_bazel_remote_execution
|
||||
- setup_bazel_rbe
|
||||
# Restore the cache before cloning the repository because the clone script re-uses
|
||||
# the restored repository if present. This reduces the amount of times the components
|
||||
# repository needs to be cloned (this is slow and increases based on commits in the repo).
|
||||
- restore_cache:
|
||||
keys:
|
||||
- *material_unit_tests_cache_key
|
||||
- *material_unit_tests_cache_key_short
|
||||
- *material_unit_tests_cache_key_fallback
|
||||
- run:
|
||||
name: "Fetching Material repository"
|
||||
command: ./scripts/ci/clone_angular_material_repo.sh
|
||||
@ -648,10 +622,12 @@ jobs:
|
||||
command: ./scripts/ci/run_angular_material_unit_tests.sh
|
||||
|
||||
test_zonejs:
|
||||
<<: *job_defaults
|
||||
executor:
|
||||
name: default-executor
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *attach_workspace
|
||||
- *init_environment
|
||||
- custom_attach_workspace
|
||||
- init_environment
|
||||
# Install
|
||||
- run: yarn --cwd packages/zone.js install --frozen-lockfile --non-interactive
|
||||
# Run zone.js tools tests
|
||||
@ -710,7 +686,9 @@ workflows:
|
||||
- test_aio_local:
|
||||
requires:
|
||||
- build-npm-packages
|
||||
- test_aio_local_ivy:
|
||||
- test_aio_local:
|
||||
name: test_aio_local_ivy
|
||||
ivy: true
|
||||
requires:
|
||||
- build-npm-packages
|
||||
- test_aio_tools:
|
||||
@ -719,7 +697,9 @@ workflows:
|
||||
- test_docs_examples:
|
||||
requires:
|
||||
- build-npm-packages
|
||||
- test_docs_examples_ivy:
|
||||
- test_docs_examples:
|
||||
name: test_docs_examples_ivy
|
||||
ivy: true
|
||||
requires:
|
||||
- build-ivy-npm-packages
|
||||
- aio_preview:
|
||||
|
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
@ -841,9 +841,7 @@ testing/** @angular/fw-test
|
||||
/aio/content/guide/updating.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/workspace-config.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/deprecations.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/migration-renderer.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/migration-undecorated-classes.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
/aio/content/guide/migration-dynamic-flag.md @angular/fw-docs-packaging @angular/framework-global-approvers @angular/framework-global-approvers-for-docs-only-changes
|
||||
|
||||
|
||||
|
||||
# ================================================
|
||||
|
17
CHANGELOG.md
17
CHANGELOG.md
@ -1,3 +1,20 @@
|
||||
<a name="8.2.11"></a>
|
||||
## [8.2.11](https://github.com/angular/angular/compare/8.2.10...8.2.11) (2019-10-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **service-worker:** continue serving api requests on cache failure ([#33165](https://github.com/angular/angular/issues/33165)) ([a2716ac](https://github.com/angular/angular/commit/a2716ac)), closes [#32996](https://github.com/angular/angular/issues/32996) [#21412](https://github.com/angular/angular/issues/21412)
|
||||
|
||||
|
||||
|
||||
<a name="8.2.10"></a>
|
||||
## [8.2.10](https://github.com/angular/angular/compare/8.2.9...8.2.10) (2019-10-09)
|
||||
|
||||
This release contains various API docs improvements.
|
||||
|
||||
|
||||
|
||||
<a name="8.2.9"></a>
|
||||
## [8.2.9](https://github.com/angular/angular/compare/8.2.8...8.2.9) (2019-10-02)
|
||||
|
||||
|
@ -201,7 +201,7 @@ Must be one of the following:
|
||||
* **test**: Adding missing tests or correcting existing tests
|
||||
|
||||
### Scope
|
||||
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages.
|
||||
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages).
|
||||
|
||||
The following is the list of supported scopes:
|
||||
|
||||
|
@ -18,7 +18,7 @@ Here are the most important tasks you might need to use:
|
||||
|
||||
* `yarn build` - create a production build of the application (after installing dependencies, boilerplate, etc).
|
||||
* `yarn build-local` - same as `build`, but use `setup-local` instead of `setup`.
|
||||
* `yarn build-with-ivy` - same as `build-local`, but in addition also turns on `ivy` mode in aio.
|
||||
* `yarn build-local-with-ivy` - same as `build-local`, but in addition also turns on `ivy` mode in aio.
|
||||
(Note: To turn on `ivy` mode in examples, see `yarn boilerplate:add` below.)
|
||||
|
||||
* `yarn start` - run a development web server that watches the files; then builds the doc-viewer and reloads the page, as necessary.
|
||||
|
@ -206,6 +206,7 @@ function heroModuleSetup() {
|
||||
nameInput.value = 'quick BROWN fOx';
|
||||
|
||||
// dispatch a DOM event so that Angular learns of input value change.
|
||||
// use newEvent utility function (not provided by Angular) for better browser compatibility
|
||||
nameInput.dispatchEvent(newEvent('input'));
|
||||
|
||||
// Tell Angular to update the display binding through the title pipe
|
||||
|
@ -28,7 +28,7 @@ export class KeyUpComponent_v1 {
|
||||
// #docregion key-up-component-1-class
|
||||
|
||||
onKey(event: KeyboardEvent) { // with type info
|
||||
this.values += (<HTMLInputElement>event.target).value + ' | ';
|
||||
this.values += (event.target as HTMLInputElement).value + ' | ';
|
||||
}
|
||||
// #docregion key-up-component-1-class-no-type
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ For example:
|
||||
}
|
||||
```
|
||||
|
||||
For more informaton, see the [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html).
|
||||
For more information, see the [TypeScript Handbook](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html).
|
||||
|
||||
## Template options
|
||||
|
||||
|
@ -93,7 +93,7 @@ In the `closed` state, shown below, the button has a height of 100 pixels, an op
|
||||
|
||||
In Angular, you can set multiple styles without any animation. However, without further refinement, the button instantly transforms with no fade, no shrinkage, or other visible indicator that a change is occurring.
|
||||
|
||||
To make the change less abrupt, we need to define an animation *transition* to specify the changes that occur between one state and another over a period of time. The `transition()` function accepts two arguments: the first argument accepts an expression that defines the direction between two transition states, and the second argument accepts an `animate()` function.
|
||||
To make the change less abrupt, we need to define an animation *transition* to specify the changes that occur between one state and another over a period of time. The `transition()` function accepts two arguments: the first argument accepts an expression that defines the direction between two transition states, and the second argument accepts one or a series of `animate()` steps.
|
||||
|
||||
|
||||
Use the `animate()` function to define the length, delay, and easing of a transition, and to designate the style function for defining styles while transitions are taking place. You can also use the `animate()` function to define the `keyframes()` function for multi-step animations. These definitions are placed in the second argument of the `animate()` function.
|
||||
|
@ -467,7 +467,7 @@ export class AppComponent {
|
||||
The collector can represent a function call or object creation with `new` as long as the syntax is valid.
|
||||
The compiler, however, can later refuse to generate a call to a _particular_ function or creation of a _particular_ object.
|
||||
|
||||
The compiler can only create instances certain classes, supports only core decorators, and only supports calls to macros (functions or static methods) that return expressions.
|
||||
The compiler can only create instances of certain classes, supports only core decorators, and only supports calls to macros (functions or static methods) that return expressions.
|
||||
* New instances
|
||||
|
||||
The compiler only allows metadata that create instances of the class `InjectionToken` from `@angular/core`.
|
||||
|
@ -142,7 +142,7 @@ Begin by adding `HostListener` to the list of imported symbols.
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
|
||||
|
||||
Then add two eventhandlers that respond when the mouse enters or leaves,
|
||||
Then add two event handlers that respond when the mouse enters or leaves,
|
||||
each adorned by the `HostListener` decorator.
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (mouse-methods)" region="mouse-methods"></code-example>
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Creating Libraries
|
||||
# Creating Libraries
|
||||
|
||||
You can create and publish new libraries to extend Angular functionality. If you find that you need to solve the same problem in more than one app (or want to share your solution with other developers), you have a candidate for a library.
|
||||
|
||||
@ -13,9 +13,15 @@ A simple example might be a button that sends users to your company website, tha
|
||||
Use the Angular CLI to generate a new library skeleton with the following command:
|
||||
|
||||
<code-example language="bash">
|
||||
ng new my-workspace --create-application=false
|
||||
cd my-workspace
|
||||
ng generate library my-lib
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<p>You can use the monorepo model to use the same workspace for multiple projects. See <a href="guide/file-structure#multiple-projects">Setting up for a multi-project workspace</a>.</p>
|
||||
</div>
|
||||
|
||||
This creates the `projects/my-lib` folder in your workspace, which contains a component and a service inside an NgModule.
|
||||
The workspace configuration file, `angular.json`, is updated with a project of type 'library'.
|
||||
|
||||
@ -207,4 +213,4 @@ This means that the TypeScript source can result in different JavaScript code in
|
||||
For this reason, an app that depends on a library should only use TypeScript path mappings that point to the *built library*.
|
||||
TypeScript path mappings should *not* point to the library source `.ts` files.
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -262,7 +262,7 @@ Providers can also be scoped by injector through constructor parameter decorator
|
||||
|
||||
</code-example>
|
||||
|
||||
Using the `@Self` decorator, the injector only looks at the component's injector for its providers. The `@SkipSelf` decorator allows you to skip the local injector and look up in the hierarchy to find a provider that satisfies this dependency. The `sessionStorageService` instance interacts with the `BrowserStorageService` using the `sessionStorage` browser API, while the `localStorageService` skips the local injector and uses the root `BrowserStorageService` that uses the `localStorage` browswer API.
|
||||
Using the `@Self` decorator, the injector only looks at the component's injector for its providers. The `@SkipSelf` decorator allows you to skip the local injector and look up in the hierarchy to find a provider that satisfies this dependency. The `sessionStorageService` instance interacts with the `BrowserStorageService` using the `sessionStorage` browser API, while the `localStorageService` skips the local injector and uses the root `BrowserStorageService` that uses the `localStorage` browser API.
|
||||
|
||||
{@a component-element}
|
||||
|
||||
|
@ -321,7 +321,7 @@ In a typical Angular project, the polyfill is not used in production builds, so
|
||||
{@a static-query-resolution}
|
||||
### `@ViewChild()` / `@ContentChild()` static resolution as the default
|
||||
|
||||
See the [dedicated migration guide for static queries](guide/static-query-migration).
|
||||
See our [dedicated migration guide for static queries](guide/static-query-migration).
|
||||
|
||||
{@a contentchild-input-together}
|
||||
### `@ContentChild()` / `@Input()` used together
|
||||
@ -389,23 +389,6 @@ As of Angular version 8, all `platform-webworker` APIs are deprecated.
|
||||
This includes both packages: `@angular/platform-webworker` and
|
||||
`@angular/platform-webworker-dynamic`.
|
||||
|
||||
|
||||
## Angular version 9 schematics
|
||||
|
||||
{@a renderer-to-renderer2}
|
||||
### Migrating from `Renderer` to `Renderer2`
|
||||
|
||||
See the [dedicated migration guide for Renderer](guide/migration-renderer).
|
||||
|
||||
{@a undecorated-classes}
|
||||
### Migrating undecorated classes
|
||||
See the [dedicated migration guide for undecorated classes](guide/migration-undecorated-classes).
|
||||
|
||||
{@a flag-migration}
|
||||
### Dynamic queries flag migration
|
||||
|
||||
See the [dedicated migration guide for dynamic queries](guide/migration-dynamic-flag).
|
||||
|
||||
{@a removed}
|
||||
## Removed APIs
|
||||
|
||||
|
@ -797,7 +797,7 @@ error.
|
||||
When you use the CLI `build` or `serve` command to build your application for different locales, change the output path using the `--outputPath` command option (along with the i18n-specific command options), so that the translation files are saved to different locations.
|
||||
When you are serving a locale-specific version from a subdirectory, you can also change the base URL used by your app by specifying the `--baseHref` option.
|
||||
|
||||
For example, if the French version of your application is served from https://myapp.com/fr/, configure the build for the French version as follows.
|
||||
For example, if the French version of your application is served from https://example.com/fr/, configure the build for the French version as follows.
|
||||
|
||||
```
|
||||
"configurations": {
|
||||
|
@ -1,107 +0,0 @@
|
||||
|
||||
# Dynamic queries flag migration
|
||||
|
||||
## What does this migration do?
|
||||
|
||||
In Angular version 8, a schematic added `static` flags to all `@ViewChild()`
|
||||
and `@ContentChild()` queries.
|
||||
This was the first step towards changing the default behavior.
|
||||
With version 9, the default value
|
||||
changes to `static: false` and the flag becomes optional.
|
||||
|
||||
This schematic scans classes in the compilation and for each
|
||||
class, checks if the members have a `@ViewChild()` or
|
||||
`@ContentChild()` query with the `static` flag set to
|
||||
`false`. If so, the schematic removes the flag, as it
|
||||
now matches the default.
|
||||
|
||||
**Before:**
|
||||
```ts
|
||||
@ViewChild('foo', {static: false}) foo: ElementRef;
|
||||
|
||||
@ViewChild('bar', {static: true}) bar: ElementRef;
|
||||
```
|
||||
|
||||
|
||||
**After:**
|
||||
```ts
|
||||
@ViewChild('foo') foo: ElementRef;
|
||||
|
||||
// this query doesn't change because the static value is true
|
||||
@ViewChild('bar', {static: true}) bar: ElementRef;
|
||||
```
|
||||
|
||||
Note that the flag is not supported in `@ViewChildren()`
|
||||
or `@ContentChildren()` queries, so the schematic
|
||||
will not check these properties.
|
||||
|
||||
|
||||
## Why is this migration necessary?
|
||||
|
||||
This schematic performs a code cleanup to remove `static`
|
||||
flags that match the default, as they are no longer
|
||||
necessary. Functionally, the code change should be a noop.
|
||||
|
||||
Before version 9, Angular figured out the static or
|
||||
dynamic nature of a query automatically, based
|
||||
on how the template was written. Looking at templates
|
||||
in this way, however, caused buggy and surprising behavior
|
||||
(see more about that in the [Static Query Migration Guide](guide/static-query-migration#what-does-this-flag-mean)).
|
||||
As of version 9, Angular uses dynamic queries
|
||||
(`static: false`) by default, which simplifies
|
||||
queries. Developers can still explicitly set a
|
||||
query to `static: true` if necessary.
|
||||
|
||||
|
||||
<div class=" alert is-helpful">
|
||||
|
||||
### What is the difference between static and dynamic queries?
|
||||
|
||||
The `static` option for `@ViewChild()` and `@ContentChild()`
|
||||
queries determines when
|
||||
the query results become available.
|
||||
|
||||
With static queries (`static: true`), the query resolves
|
||||
once the view has been created, but before change detection runs.
|
||||
The result, though, will never be updated to reflect
|
||||
changes to your view, such as
|
||||
changes to `ngIf` and `ngFor` blocks.
|
||||
|
||||
With dynamic queries (`static: false`), the query resolves
|
||||
after either `ngAfterViewInit()` or
|
||||
`ngAfterContentInit()` for `@ViewChild()` and `@ContentChild()`
|
||||
respectively. The result will
|
||||
be updated for changes to your view, such as changes to
|
||||
`ngIf` and `ngFor` blocks.
|
||||
|
||||
For more information, see the following entries in the
|
||||
[Static Query Migration Guide](https://angular.io/guide/static-query-migration):
|
||||
|
||||
* [How do I choose which `static` flag value to use: `true` or `false`?](https://angular.io/guide/static-query-migration#how-do-i-choose-which-static-flag-value-to-use-true-or-false)
|
||||
|
||||
* [Is there a case where I should use `{static: true}`?](https://angular.io/guide/static-query-migration#is-there-a-case-where-i-should-use-static-true)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
## What does this mean for libraries?
|
||||
|
||||
In order to support applications that are still running
|
||||
with version 8, the safest option for libraries is to
|
||||
retain the `static` flag to keep the resolution
|
||||
timing consistent.
|
||||
|
||||
- *Libraries on version 9 with applications running version 8: *
|
||||
|
||||
The schematic won't run on libraries. As long as libraries retain their `static` flags from version 8, they should work with apps on 8.
|
||||
|
||||
- *Libraries on version 8 with applications running version 9: *
|
||||
|
||||
Libraries will have explicit flags defined. The behavior
|
||||
with explicit flags has not changed.
|
||||
|
||||
|
||||
### What about applications using non-migrated libraries?
|
||||
|
||||
Because this is a code cleanup that is a noop,
|
||||
non-migrated libraries will work the same either way.
|
@ -1,96 +0,0 @@
|
||||
# `Renderer` to `Renderer2` migration
|
||||
|
||||
## Migration Overview
|
||||
|
||||
The `Renderer` class has been marked as deprecated since Angular version 4. This section provides guidance on migrating from this deprecated API to the newer `Renderer2` API and what it means for your app.
|
||||
|
||||
## Why should I migrate to Renderer2?
|
||||
|
||||
The deprecated `Renderer` class has been removed in version 9 of Angular, so it's necessary to migrate to a supported API. Using `Renderer2` is the recommended strategy because it supports a similar set of functionality to `Renderer`. The API surface is quite large (with 19 methods), but the schematic should simplify this process for your applications.
|
||||
|
||||
## Is there action required on my end?
|
||||
|
||||
No. The schematic should handle most cases with the exception of `Renderer.animate()` and `Renderer.setDebugInfo()`, which already aren’t supported.
|
||||
|
||||
## What are the `__ngRendererX` methods? Why are they necessary?
|
||||
|
||||
Some methods either don't have exact equivalents in `Renderer2`, or they correspond to more than one expression. For example, both renderers have a `createElement()` method, but they're not equal because a call such as `renderer.createElement(parentNode, namespaceAndName)` in the `Renderer` corresponds to the following block of code in `Renderer2`:
|
||||
|
||||
```ts
|
||||
const [namespace, name] = splitNamespace(namespaceAndName);
|
||||
const el = renderer.createElement(name, namespace);
|
||||
if (parentNode) {
|
||||
renderer.appendChild(parentNode, el);
|
||||
}
|
||||
return el;
|
||||
```
|
||||
|
||||
Migration has to guarantee that the return values of functions and types of variables stay the same. To handle the majority of cases safely, the schematic declares helper functions at the bottom of the user's file. These helpers encapsulate your own logic and keep the replacements inside your code down to a single function call. Here's an example of how the `createElement()` migration looks:
|
||||
|
||||
|
||||
**Before:**
|
||||
|
||||
```ts
|
||||
public createAndAppendElement() {
|
||||
const el = this.renderer.createElement('span');
|
||||
el.textContent = 'hello world';
|
||||
return el;
|
||||
}
|
||||
```
|
||||
|
||||
**After:**
|
||||
|
||||
<code-example>
|
||||
|
||||
public createAndAppendElement() {
|
||||
const el = __ngRendererCreateElement(this.renderer, this.element, 'span');
|
||||
el.textContent = 'hello world';
|
||||
return el;
|
||||
}
|
||||
// Generated code at the bottom of the file
|
||||
__ngRendererCreateElement(renderer: any, parentNode: any, nameAndNamespace: any) {
|
||||
const [namespace, name] = __ngRendererSplitNamespace(namespaceAndName);
|
||||
const el = renderer.createElement(name, namespace);
|
||||
if (parentNode) {
|
||||
renderer.appendChild(parentNode, el);
|
||||
}
|
||||
return el;
|
||||
}
|
||||
__ngRendererSplitNamespace(nameAndNamespace: any) {
|
||||
// returns the split name and namespace
|
||||
}
|
||||
|
||||
</code-example>
|
||||
|
||||
When implementing these helper functions, the schematic ensures that they're only declared once per file and that their names are unique enough that there's a small chance of colliding with pre-existing functions in your code. The schematic also keeps their parameter types as `any` so that it doesn't have to insert extra logic that ensures that their values have the correct type.
|
||||
|
||||
### I’m a library author. Should I run this migration?
|
||||
|
||||
**Library authors should definitely use this migration to move away from the `Renderer`. Otherwise, the libraries won't work with applications built with version 9.**
|
||||
|
||||
|
||||
### Full list of method migrations
|
||||
|
||||
The following table shows all methods that the migration maps from `Renderer` to `Renderer2`.
|
||||
|
||||
|Renderer|Renderer2|
|
||||
|---|---|
|
||||
|`listen(renderElement, name, callback)`|`listen(renderElement, name, callback)`|
|
||||
|`setElementProperty(renderElement, propertyName, propertyValue)`|`setProperty(renderElement, propertyName, propertyValue)`|
|
||||
|`setText(renderNode, text)`|`setValue(renderNode, text)`|
|
||||
|`listenGlobal(target, name, callback)`|`listen(target, name, callback)`|
|
||||
|`selectRootElement(selectorOrNode, debugInfo?)`|`selectRootElement(selectorOrNode)`|
|
||||
|`createElement(parentElement, name, debugInfo?)`|`appendChild(parentElement, createElement(name))`|
|
||||
|`setElementStyle(el, style, value?)`|`value == null ? removeStyle(el, style) : setStyle(el, style, value)`
|
||||
|`setElementAttribute(el, name, value?)`|`attributeValue == null ? removeAttribute(el, name) : setAttribute(el, name, value)`
|
||||
|`createText(parentElement, value, debugInfo?)`|`appendChild(parentElement, createText(value))`|
|
||||
|`createTemplateAnchor(parentElement)`|`appendChild(parentElement, createComment(''))`|
|
||||
|`setElementClass(renderElement, className, isAdd)`|`isAdd ? addClass(renderElement, className) : removeClass(renderElement, className)`|
|
||||
|`projectNodes(parentElement, nodes)`|`for (let i = 0; i < nodes.length; i<ins></ins>) { appendChild(parentElement, nodes<i>); }`|
|
||||
|`attachViewAfter(node, viewRootNodes)`|`const parentElement = parentNode(node); const nextSibling = nextSibling(node); for (let i = 0; i < viewRootNodes.length; i<ins></ins>) { insertBefore(parentElement, viewRootNodes<i>, nextSibling);}`|
|
||||
|`detachView(viewRootNodes)`|`for (let i = 0; i < viewRootNodes.length; i<ins></ins>) {const node = viewRootNodes<i>; const parentElement = parentNode(node); removeChild(parentElement, node);}`|
|
||||
|`destroyView(hostElement, viewAllNodes)`|`for (let i = 0; i < viewAllNodes.length; i<ins></ins>) { destroyNode(viewAllNodes<i>); }`|
|
||||
|`setBindingDebugInfo()`|This function is a noop in `Renderer2`.|
|
||||
|`createViewRoot(hostElement)`|Should be replaced with a reference to `hostElement`|
|
||||
|`invokeElementMethod(renderElement, methodName, args?)`|`(renderElement as any)<methodName>.apply(renderElement, args);`|
|
||||
|`animate(element, startingStyles, keyframes, duration, delay, easing, previousPlayers?)`|Throws an error (same behavior as `Renderer.animate()`)|
|
@ -1,138 +0,0 @@
|
||||
# Undecorated classes migration (DI)
|
||||
|
||||
This section discusses an Angular version 9 schematic that migrates
|
||||
two inheritance patterns that need to be updated to work with Ivy.
|
||||
|
||||
## What does this migration do?
|
||||
|
||||
This migration adds an empty `@Directive()` decorator to undecorated
|
||||
base classes that are extended by either directives or components.
|
||||
|
||||
Before:
|
||||
```ts
|
||||
export class BaseMenu {
|
||||
constructor(private vcr: ViewContainerRef) {}
|
||||
}
|
||||
|
||||
@Directive({selector: '[settingsMenu]'})
|
||||
export class SettingsMenu extends BaseMenu {}
|
||||
```
|
||||
|
||||
After:
|
||||
```ts
|
||||
@Directive()
|
||||
export class BaseMenu {
|
||||
constructor(private vcr: ViewContainerRef) {}
|
||||
}
|
||||
|
||||
@Directive({selector: '[settingsMenu]'})
|
||||
export class SettingsMenu extends BaseMenu {}
|
||||
```
|
||||
|
||||
The schematic also copies any inherited directive or component metadata to the derived class.
|
||||
|
||||
Before:
|
||||
```ts
|
||||
@Component({
|
||||
selector: 'base-menu',
|
||||
template: '<div></div>'
|
||||
})
|
||||
class BaseMenu {}
|
||||
|
||||
export class SettingsMenu extends BaseMenu {}
|
||||
```
|
||||
|
||||
After:
|
||||
```ts
|
||||
@Component({
|
||||
selector: 'base-menu',
|
||||
template: '<div></div>'
|
||||
})
|
||||
class BaseMenu {}
|
||||
|
||||
@Component({
|
||||
selector: 'settings-menu',
|
||||
template: '<div></div>'
|
||||
})
|
||||
export class SettingsMenu extends BaseMenu {}
|
||||
```
|
||||
|
||||
## Why is this migration necessary?
|
||||
|
||||
When a class has a `@Directive()` or `@Component()` decorator,
|
||||
the Angular compiler generates extra code to inject dependencies into
|
||||
the constructor. When using inheritance, Ivy needs both the parent class
|
||||
and the child class to apply a decorator to generate the correct code.
|
||||
|
||||
You can think of this change as two cases: a parent class is missing a
|
||||
decorator or a child class is missing a decorator. In both scenarios,
|
||||
Angular's run-time needs additional information from the compiler.
|
||||
This additional information comes from adding decorators.
|
||||
|
||||
|
||||
### Decorator missing from parent class
|
||||
|
||||
When the decorator is missing from the parent class,
|
||||
the subclass will inherit a constructor from a class for
|
||||
which the compiler did not generate special constructor
|
||||
info (because it was not decorated as a directive).
|
||||
When Angular then tries to create the subclass,
|
||||
it doesn't have the correct info
|
||||
to create it.
|
||||
|
||||
In View Engine, the compiler has global knowledge, so it
|
||||
can look up the missing data. However, the Ivy compiler
|
||||
only processes each directive in isolation. This means that
|
||||
compilation can be faster, but the compiler can't
|
||||
automatically infer the same
|
||||
information as before. Adding the `@Directive()` explicitly
|
||||
provides this information.
|
||||
|
||||
In the future, add `@Directive()` to base classes that
|
||||
do not already have decorators and are extended by directives.
|
||||
|
||||
### Decorator missing from child class
|
||||
|
||||
When the child class is missing the decorator, the
|
||||
child class inherits from the
|
||||
parent class yet has no decorators of its own.
|
||||
Without a decorator, the compiler has no way of knowing
|
||||
that the class is a `@Directive` or `@Component`, so
|
||||
it doesn't generate the proper instructions for the directive.
|
||||
|
||||
|
||||
## What does it mean to have a `@Directive()` decorator with no metadata inside of it?
|
||||
|
||||
The presence of the `@Directive` decorator causes Angular to generate
|
||||
extra code for the affected class. If that decorator includes no
|
||||
properties (metadata),
|
||||
the directive won't be matched to elements or instantiated
|
||||
directly, but other classes that _extend_ the
|
||||
directive class will inherit this generated code. You can think of
|
||||
this as an "abstract" directive.
|
||||
|
||||
Adding an abstract directive to an `NgModule` will cause an error.
|
||||
A directive must have a `selector` property defined in order to match some element in a template.
|
||||
|
||||
## When do I need a `@Directive()` decorator without a selector?
|
||||
|
||||
If you're using dependency injection, or any Angular-specific
|
||||
feature, such as `@HostBinding()`, `@ViewChild()`, or `@Input()`, you need a
|
||||
`@Directive()` or `@Component()` decorator.
|
||||
The decorator lets the compiler know to generate the correct
|
||||
instructions to create that class and any classes that extend it.
|
||||
If you don't want to use that base class as a directive directly, leave
|
||||
the selector blank. If you do want it to be usable independently,
|
||||
fill in the metadata as usual.
|
||||
|
||||
Classes that don't use Angular features don't need an Angular decorator.
|
||||
|
||||
## I'm a library author. Should I add the `@Directive()` decorator to base classes?
|
||||
|
||||
|
||||
As support for selectorless decorators is introduced in
|
||||
Angular version 9, if you want to support Angular version 8 and earlier, you
|
||||
shouldn't add a selectorless `@Directive()` decorator.
|
||||
You can either add `@Directive()` with a selector or
|
||||
add an explicit constructor to affected subclasses.
|
||||
|
@ -352,7 +352,7 @@ AngularJS and Angular approaches. Here's what happens:
|
||||
</figure>
|
||||
|
||||
In practice, you do not need to call `$apply()`,
|
||||
regardless of whether it is in AngularJS on Angular. The
|
||||
regardless of whether it is in AngularJS or Angular. The
|
||||
`UpgradeModule` does it for us. You *can* still call `$apply()` so there
|
||||
is no need to remove such calls from existing code. Those calls just trigger
|
||||
additional AngularJS change detection checks in a hybrid application.
|
||||
|
@ -674,6 +674,13 @@
|
||||
"rev": true,
|
||||
"title": "MDB Angular Boilerplate",
|
||||
"url": "https://github.com/mdbootstrap/Angular-Bootstrap-Boilerplate"
|
||||
},
|
||||
"dotnettricks": {
|
||||
"desc": "Online videos and training for Angular.",
|
||||
"logo": "",
|
||||
"rev": true,
|
||||
"title": "DotNetTricks",
|
||||
"url": "https://www.dotnettricks.com/courses/angular"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -50,6 +50,12 @@ ng build --prod
|
||||
|
||||
This will produce the files that you need to deploy.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
If the above `ng build` command throws an error about missing packages, append the missing dependencies in your local project's `package.json` file to match the one in the downloaded StackBlitz project.
|
||||
|
||||
</div>
|
||||
|
||||
#### Hosting the built project
|
||||
|
||||
The files in the `dist/my-project-name` folder are static and can be hosted on any web server capable of serving files (`Node.js`, Java, .NET) or any backend (Firebase, Google Cloud, App Engine, others).
|
||||
|
@ -90,8 +90,16 @@ The product details component handles the display of each product. The Angular R
|
||||
<code-example path="getting-started/src/app/product-details/product-details.component.1.ts" region="get-product">
|
||||
</code-example>
|
||||
|
||||
Angular calls `ngOnInit()` shortly after creating a component.
|
||||
|
||||
The route parameters correspond to the path variables defined in the route. The `productId` is provided from
|
||||
the URL that was matched to the route. You use the `productId` to display the details for each unique product.
|
||||
the URL that was matched to the route. You use the `productId` to display the details for each unique product.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For more information on `ngOnInit()`, see [Lifecycle hooks](guide/lifecycle-hooks).
|
||||
|
||||
</div>
|
||||
|
||||
1. Update the template to display product details information inside an `*ngIf`.
|
||||
|
||||
|
@ -19,10 +19,10 @@
|
||||
"build-local": "yarn ~~build",
|
||||
"prebuild-local-ci": "yarn setup-local --no-build-packages",
|
||||
"build-local-ci": "yarn ~~build --progress=false",
|
||||
"prebuild-with-ivy": "yarn setup-local && node scripts/switch-to-ivy",
|
||||
"build-with-ivy": "yarn ~~build",
|
||||
"prebuild-with-ivy-ci": "yarn setup-local --no-build-packages && node scripts/switch-to-ivy",
|
||||
"build-with-ivy-ci": "yarn ~~build --progress=false",
|
||||
"prebuild-local-with-ivy": "yarn setup-local && node scripts/switch-to-ivy",
|
||||
"build-local-with-ivy": "yarn ~~build",
|
||||
"prebuild-local-with-ivy-ci": "yarn setup-local --no-build-packages && node scripts/switch-to-ivy",
|
||||
"build-local-with-ivy-ci": "yarn ~~build --progress=false",
|
||||
"extract-cli-command-docs": "node tools/transforms/cli-docs-package/extract-cli-commands.js e21aeeecd",
|
||||
"lint": "yarn check-env && yarn docs-lint && ng lint && yarn example-lint && yarn tools-lint",
|
||||
"test": "yarn check-env && ng test",
|
||||
@ -115,7 +115,7 @@
|
||||
"chalk": "^2.1.0",
|
||||
"chrome-launcher": "^0.10.7",
|
||||
"cjson": "^0.5.0",
|
||||
"codelyzer": "^5.0.0",
|
||||
"codelyzer": "^5.1.1",
|
||||
"cross-spawn": "^5.1.0",
|
||||
"css-selector-parser": "^1.3.0",
|
||||
"dgeni": "^0.4.11",
|
||||
|
@ -7,10 +7,11 @@ import { LocationService } from 'app/shared/location.service';
|
||||
selector: `aio-contributor-list`,
|
||||
template: `
|
||||
<div class="flex-center group-buttons">
|
||||
<a *ngFor="let name of groupNames"
|
||||
[class.selected]="name == selectedGroup.name"
|
||||
class="button mat-button filter-button"
|
||||
(click)="selectGroup(name)">{{name}}</a>
|
||||
<a *ngFor="let name of groupNames"
|
||||
[class.selected]="name == selectedGroup.name"
|
||||
class="button mat-button filter-button"
|
||||
(click)="selectGroup(name)"
|
||||
(keyup.enter)="selectGroup(name)">{{name}}</a>
|
||||
</div>
|
||||
<section *ngIf="selectedGroup" class="grid-fluid">
|
||||
<div class="contributor-group">
|
||||
|
@ -8,7 +8,7 @@ import { CONTENT_URL_PREFIX } from 'app/documents/document.service';
|
||||
template: `
|
||||
<div [ngClass]="{ 'flipped': person.isFlipped }" class="contributor-card">
|
||||
|
||||
<div class="card-front" (click)="flipCard(person)">
|
||||
<div class="card-front" (click)="flipCard(person)" (keyup.enter)="flipCard(person)">
|
||||
<h3>{{person.name}}</h3>
|
||||
|
||||
<div class="contributor-image" [style.background-image]="'url('+pictureBase+(person.picture || noPicture)+')'">
|
||||
@ -28,7 +28,7 @@ import { CONTENT_URL_PREFIX } from 'app/documents/document.service';
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-back" *ngIf="person.isFlipped" (click)="flipCard(person)">
|
||||
<div class="card-back" *ngIf="person.isFlipped" (click)="flipCard(person)" (keyup.enter)="flipCard(person)">
|
||||
<h3>{{person.name}}</h3>
|
||||
<p class="contributor-bio">{{person.bio}}</p>
|
||||
</div>
|
||||
|
@ -4,6 +4,7 @@ import { PlatformLocation } from '@angular/common';
|
||||
import { Category } from './resource.model';
|
||||
import { ResourceService } from './resource.service';
|
||||
|
||||
/* tslint:disable:template-accessibility-elements-content */
|
||||
@Component({
|
||||
selector: 'aio-resource-list',
|
||||
templateUrl: 'resource-list.component.html'
|
||||
|
@ -1,4 +1,4 @@
|
||||
<span class="content" (click)="contentClick()">
|
||||
<span class="content" (click)="contentClick()" (keyup.enter)="contentClick()">
|
||||
<ng-content></ng-content>
|
||||
</span>
|
||||
|
||||
|
@ -21,16 +21,6 @@
|
||||
/** IE10 and IE11 requires the following for NgClass support on SVG elements */
|
||||
// import 'classlist.js'; // Run `npm install --save classlist.js`.
|
||||
|
||||
/** IE10 and IE11 requires the following for the Reflect API. */
|
||||
/**
|
||||
* DO NOT REMOVE
|
||||
* By default, Reflect polyfills are auto-included by the CLI and
|
||||
* are required for JIT compilation. StackBlitz examples are
|
||||
* compiled using JIT.
|
||||
*/
|
||||
import 'core-js/es6/reflect';
|
||||
import 'core-js/es7/reflect';
|
||||
|
||||
/**
|
||||
* Web Animations `@angular/platform-browser/animations`
|
||||
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
|
||||
@ -65,8 +55,7 @@ import 'core-js/es7/reflect';
|
||||
/***************************************************************************************************
|
||||
* Zone JS is required by default for Angular itself.
|
||||
*/
|
||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
import 'zone.js/dist/zone-patch-canvas';
|
||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
|
||||
/***************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
|
@ -33,7 +33,7 @@
|
||||
"@nguniversal/express-engine": "^8.0.0-rc.1",
|
||||
"@nguniversal/module-map-ngfactory-loader": "^8.0.0-rc.1",
|
||||
"angular": "1.7.8",
|
||||
"angular-in-memory-web-api": "github:brandonroberts/in-memory-web-api-bazel#50a34d8",
|
||||
"angular-in-memory-web-api": "^0.9.0",
|
||||
"angular-route": "1.7.8",
|
||||
"core-js": "^2.5.4",
|
||||
"express": "^4.14.1",
|
||||
@ -69,7 +69,7 @@
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-coverage-istanbul-reporter": "~2.0.1",
|
||||
"karma-jasmine": "~2.0.1",
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
"karma-jasmine-html-reporter": "^1.4.2",
|
||||
"lite-server": "^2.2.2",
|
||||
"lodash": "^4.16.2",
|
||||
"protractor": "~5.4.0",
|
||||
|
@ -795,11 +795,10 @@ amdefine@>=0.0.4:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
|
||||
|
||||
"angular-in-memory-web-api@github:brandonroberts/in-memory-web-api-bazel#50a34d8":
|
||||
version "0.8.0"
|
||||
resolved "https://codeload.github.com/brandonroberts/in-memory-web-api-bazel/tar.gz/50a34d84b627ec88816242dec77603d6dcb9c880"
|
||||
dependencies:
|
||||
tslib "^1.9.0"
|
||||
angular-in-memory-web-api@^0.9.0:
|
||||
version "0.9.0"
|
||||
resolved "https://registry.yarnpkg.com/angular-in-memory-web-api/-/angular-in-memory-web-api-0.9.0.tgz#6c98d9494fadc6b98f54e68376a1998ccfff04bc"
|
||||
integrity sha512-//PiJ5qb1+Yf/N7270ioQqR2laf4/Irjavg+M+WEn8y4At9LUoYgbQ5HVwvM5xUTlVlL0XkbJRLxREcGGNdIEw==
|
||||
|
||||
angular-route@1.7.8:
|
||||
version "1.7.8"
|
||||
@ -4430,15 +4429,10 @@ karma-coverage-istanbul-reporter@~2.0.1:
|
||||
istanbul-api "^2.0.5"
|
||||
minimatch "^3.0.4"
|
||||
|
||||
karma-jasmine-html-reporter@^0.2.2:
|
||||
version "0.2.2"
|
||||
resolved "https://registry.yarnpkg.com/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-0.2.2.tgz#48a8e5ef18807617ee2b5e33c1194c35b439524c"
|
||||
dependencies:
|
||||
karma-jasmine "^1.0.2"
|
||||
|
||||
karma-jasmine@^1.0.2:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.1.0.tgz#22e4c06bf9a182e5294d1f705e3733811b810acf"
|
||||
karma-jasmine-html-reporter@^1.4.2:
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.4.2.tgz#16d100fd701271192d27fd28ddc90b710ad36fff"
|
||||
integrity sha512-7g0gPj8+9JepCNJR9WjDyQ2RkZ375jpdurYQyAYv8PorUCadepl8vrD6LmMqOGcM17cnrynBawQYZHaumgDjBw==
|
||||
|
||||
karma-jasmine@~2.0.1:
|
||||
version "2.0.1"
|
||||
|
@ -29,6 +29,7 @@ module.exports =
|
||||
.processor(require('./processors/processClassLikeMembers'))
|
||||
.processor(require('./processors/markBarredODocsAsPrivate'))
|
||||
.processor(require('./processors/filterPrivateDocs'))
|
||||
.processor(require('./processors/filterMembers'))
|
||||
.processor(require('./processors/computeSearchTitle'))
|
||||
.processor(require('./processors/simplifyMemberAnchors'))
|
||||
.processor(require('./processors/computeStability'))
|
||||
@ -176,6 +177,12 @@ module.exports =
|
||||
filterContainedDocs.docTypes = API_CONTAINED_DOC_TYPES;
|
||||
})
|
||||
|
||||
.config(function(filterMembers) {
|
||||
filterMembers.notAllowedPatterns.push(
|
||||
/^ng[A-Z].*Def$/
|
||||
);
|
||||
})
|
||||
|
||||
|
||||
.config(function(computePathsProcessor, EXPORT_DOC_TYPES, generateApiListDoc) {
|
||||
|
||||
|
21
aio/tools/transforms/angular-api-package/processors/filterMembers.js
vendored
Normal file
21
aio/tools/transforms/angular-api-package/processors/filterMembers.js
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Filter out members (i.e. static and instance properties and methods) that match specific
|
||||
* patterns. Patterns can be added (as `RegExp`s) to the `notAllowedPatterns` array.
|
||||
*
|
||||
* (By default, no members are excluded.)
|
||||
*/
|
||||
module.exports = function filterMembers() {
|
||||
return {
|
||||
$runAfter: ['processing-docs'],
|
||||
$runBefore: ['docs-processed'],
|
||||
notAllowedPatterns: [],
|
||||
$process(docs) {
|
||||
const isAllowed = ({name}) => !this.notAllowedPatterns.some(re => re.test(name));
|
||||
|
||||
docs.forEach(doc => {
|
||||
if (doc.statics) doc.statics = doc.statics.filter(isAllowed);
|
||||
if (doc.members) doc.members = doc.members.filter(isAllowed);
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
@ -0,0 +1,102 @@
|
||||
const processorFactory = require('./filterMembers');
|
||||
const testPackage = require('../../helpers/test-package');
|
||||
const Dgeni = require('dgeni');
|
||||
|
||||
describe('filterMembers processor', () => {
|
||||
|
||||
it('should be available on the injector', () => {
|
||||
const dgeni = new Dgeni([testPackage('angular-api-package')]);
|
||||
const injector = dgeni.configureInjector();
|
||||
const processor = injector.get('filterMembers');
|
||||
expect(processor.$process).toBeDefined();
|
||||
expect(processor.$runAfter).toEqual(['processing-docs']);
|
||||
expect(processor.$runBefore).toEqual(['docs-processed']);
|
||||
});
|
||||
|
||||
it('should remove members that match one of the not allowed patterns', () => {
|
||||
const processor = processorFactory();
|
||||
processor.notAllowedPatterns = [/^foo/, /bar$/];
|
||||
const docs = [
|
||||
// Doc without members.
|
||||
{ },
|
||||
|
||||
// Doc with static members only.
|
||||
{
|
||||
statics: [
|
||||
{ name: 'fooStatic' }, // Will be removed.
|
||||
{ name: 'FOOStatic' },
|
||||
{ name: 'barStatic' },
|
||||
{ name: 'statiCbar' }, // Will be removed.
|
||||
],
|
||||
},
|
||||
|
||||
// Doc with instance members only.
|
||||
{
|
||||
members: [
|
||||
{ name: 'fooInstance' }, // Will be removed.
|
||||
{ name: 'FOOInstance' },
|
||||
{ name: 'barInstance' },
|
||||
{ name: 'instancEbar' }, // Will be removed.
|
||||
],
|
||||
},
|
||||
|
||||
// Doc with both static and instance members.
|
||||
{
|
||||
statics: [
|
||||
{ name: 'fooStatic' }, // Will be removed.
|
||||
{ name: 'FOOStatic' },
|
||||
{ name: 'barStatic' },
|
||||
{ name: 'statiCbar' }, // Will be removed.
|
||||
],
|
||||
members: [
|
||||
{ name: 'fooInstance' }, // Will be removed.
|
||||
{ name: 'FOOInstance' },
|
||||
{ name: 'barInstance' },
|
||||
{ name: 'instancEbar' }, // Will be removed.
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
processor.$process(docs);
|
||||
|
||||
expect(docs).toEqual([
|
||||
{ },
|
||||
{
|
||||
statics: [ { name: 'FOOStatic' }, { name: 'barStatic' } ],
|
||||
},
|
||||
{
|
||||
members: [ { name: 'FOOInstance' }, { name: 'barInstance' } ],
|
||||
},
|
||||
{
|
||||
statics: [ { name: 'FOOStatic' }, { name: 'barStatic' } ],
|
||||
members: [ { name: 'FOOInstance' }, { name: 'barInstance' } ],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should remove no members by default', () => {
|
||||
const processor = processorFactory();
|
||||
const expectedDocs = [
|
||||
{
|
||||
statics: [
|
||||
{ name: '' },
|
||||
{ name: 'foo' },
|
||||
{ name: '__bar' },
|
||||
{ name: 'ngBazDef' },
|
||||
],
|
||||
members: [
|
||||
{ name: '' },
|
||||
{ name: 'foo' },
|
||||
{ name: '__bar' },
|
||||
{ name: 'ngBazDef' },
|
||||
],
|
||||
},
|
||||
];
|
||||
const actualDocs = JSON.parse(JSON.stringify(expectedDocs));
|
||||
|
||||
processor.$process(actualDocs);
|
||||
|
||||
expect(processor.notAllowedPatterns).toEqual([]);
|
||||
expect(actualDocs).toEqual(expectedDocs);
|
||||
});
|
||||
});
|
@ -96,6 +96,16 @@
|
||||
"ban-keywords",
|
||||
"check-format",
|
||||
"require-const-for-all-caps"
|
||||
]
|
||||
],
|
||||
"template-accessibility-alt-text": true,
|
||||
"template-accessibility-elements-content": true,
|
||||
"template-accessibility-label-for": true,
|
||||
"template-accessibility-tabindex-no-positive": true,
|
||||
"template-accessibility-table-scope": true,
|
||||
"template-accessibility-valid-aria": true,
|
||||
"template-click-events-have-key-events": true,
|
||||
"template-mouse-events-have-key-events": true,
|
||||
"template-no-autofocus": true,
|
||||
"template-no-distracting-elements": true
|
||||
}
|
||||
}
|
||||
|
@ -938,10 +938,10 @@ anymatch@^3.0.1:
|
||||
normalize-path "^3.0.0"
|
||||
picomatch "^2.0.4"
|
||||
|
||||
app-root-path@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.1.0.tgz#98bf6599327ecea199309866e8140368fd2e646a"
|
||||
integrity sha1-mL9lmTJ+zqGZMJhm6BQDaP0uZGo=
|
||||
app-root-path@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-2.2.1.tgz#d0df4a682ee408273583d43f6f79e9892624bc9a"
|
||||
integrity sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA==
|
||||
|
||||
append-transform@^1.0.0:
|
||||
version "1.0.0"
|
||||
@ -2179,12 +2179,12 @@ code-point-at@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
|
||||
integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
|
||||
|
||||
codelyzer@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/codelyzer/-/codelyzer-5.0.0.tgz#e4032efb23a7c5d4bcfe7321fc1789490c679837"
|
||||
integrity sha512-Bif70XYt8NFf/Q9GPTxmC86OsBRfQZq1dBjdruJ5kZhJ8/jKhJL6MvCLKnYtSOG6Rhiv/44DU0cHk6GYthjy8Q==
|
||||
codelyzer@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/codelyzer/-/codelyzer-5.1.1.tgz#a599fa8c2a5847f553a792b934e493d1506a4a62"
|
||||
integrity sha512-t8ZLSZBUjVFOJVk4jASLgmTdKWK/0ZsQCnPXy6PXw1LWOOormQOVnyy4OYoiZ6rAWTrz60Obx+zA2t8xY53QzQ==
|
||||
dependencies:
|
||||
app-root-path "^2.1.0"
|
||||
app-root-path "^2.2.1"
|
||||
aria-query "^3.0.0"
|
||||
axobject-query "^2.0.2"
|
||||
css-selector-tokenizer "^0.7.1"
|
||||
|
@ -26,7 +26,7 @@ following products on your development machine:
|
||||
|
||||
* [Yarn](https://yarnpkg.com) (version specified in the engines field of [`package.json`](../package.json)) which is used to install dependencies.
|
||||
|
||||
* [Java Development Kit](http://www.oracle.com/technetwork/es/java/javase/downloads/index.html) which is used
|
||||
* [Java Development Kit](https://www.oracle.com/technetwork/java/javase/downloads/index.html) which is used
|
||||
to execute the selenium standalone server for e2e testing.
|
||||
|
||||
## Getting the Sources
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "8.2.9",
|
||||
"version": "8.2.11",
|
||||
"private": true,
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
"homepage": "https://github.com/angular/angular",
|
||||
|
@ -372,7 +372,8 @@ def ngc_compile_action(
|
||||
node_opts,
|
||||
locale = None,
|
||||
i18n_args = [],
|
||||
dts_bundles_out = None):
|
||||
dts_bundles_out = None,
|
||||
compile_mode = "prodmode"):
|
||||
"""Helper function to create the ngc action.
|
||||
|
||||
This is exposed for google3 to wire up i18n replay rules, and is not intended
|
||||
@ -397,13 +398,13 @@ def ngc_compile_action(
|
||||
is_legacy_ngc = _is_legacy_ngc(ctx)
|
||||
|
||||
mnemonic = "AngularTemplateCompile"
|
||||
progress_message = "Compiling Angular templates (%s) %s" % (_compiler_name(ctx), label)
|
||||
progress_message = "Compiling Angular templates (%s - %s) %s" % (_compiler_name(ctx), compile_mode, label)
|
||||
|
||||
if locale:
|
||||
mnemonic = "AngularI18NMerging"
|
||||
supports_workers = "0"
|
||||
progress_message = ("Recompiling Angular templates (ngc) %s for locale %s" %
|
||||
(label, locale))
|
||||
progress_message = ("Recompiling Angular templates (ngc - %s) %s for locale %s" %
|
||||
(compile_mode, label, locale))
|
||||
else:
|
||||
supports_workers = str(int(ctx.attr._supports_workers))
|
||||
|
||||
@ -463,7 +464,7 @@ def ngc_compile_action(
|
||||
dts_entry_points.append(_R3_SYMBOLS_DTS_FILE)
|
||||
|
||||
ctx.actions.run(
|
||||
progress_message = "Bundling DTS %s" % str(ctx.label),
|
||||
progress_message = "Bundling DTS (%s) %s" % (compile_mode, str(ctx.label)),
|
||||
mnemonic = "APIExtractor",
|
||||
executable = ctx.executable.api_extractor,
|
||||
inputs = filter_inputs,
|
||||
@ -495,7 +496,15 @@ def _filter_ts_inputs(all_inputs):
|
||||
if f.path.endswith(".js") or f.path.endswith(".ts") or f.path.endswith(".json")
|
||||
]
|
||||
|
||||
def _compile_action(ctx, inputs, outputs, dts_bundles_out, messages_out, tsconfig_file, node_opts):
|
||||
def _compile_action(
|
||||
ctx,
|
||||
inputs,
|
||||
outputs,
|
||||
dts_bundles_out,
|
||||
messages_out,
|
||||
tsconfig_file,
|
||||
node_opts,
|
||||
compile_mode):
|
||||
# Give the Angular compiler all the user-listed assets
|
||||
file_inputs = list(ctx.files.assets)
|
||||
|
||||
@ -533,16 +542,16 @@ def _compile_action(ctx, inputs, outputs, dts_bundles_out, messages_out, tsconfi
|
||||
],
|
||||
)
|
||||
|
||||
return ngc_compile_action(ctx, ctx.label, action_inputs, outputs, messages_out, tsconfig_file, node_opts, None, [], dts_bundles_out)
|
||||
return ngc_compile_action(ctx, ctx.label, action_inputs, outputs, messages_out, tsconfig_file, node_opts, None, [], dts_bundles_out, compile_mode)
|
||||
|
||||
def _prodmode_compile_action(ctx, inputs, outputs, tsconfig_file, node_opts):
|
||||
outs = _expected_outs(ctx)
|
||||
return _compile_action(ctx, inputs, outputs + outs.closure_js, None, outs.i18n_messages, tsconfig_file, node_opts)
|
||||
return _compile_action(ctx, inputs, outputs + outs.closure_js, None, outs.i18n_messages, tsconfig_file, node_opts, "prodmode")
|
||||
|
||||
def _devmode_compile_action(ctx, inputs, outputs, tsconfig_file, node_opts):
|
||||
outs = _expected_outs(ctx)
|
||||
compile_action_outputs = outputs + outs.devmode_js + outs.declarations + outs.summaries + outs.metadata
|
||||
_compile_action(ctx, inputs, compile_action_outputs, outs.dts_bundles, None, tsconfig_file, node_opts)
|
||||
_compile_action(ctx, inputs, compile_action_outputs, outs.dts_bundles, None, tsconfig_file, node_opts, "devmode")
|
||||
|
||||
def _ts_expected_outs(ctx, label, srcs_files = []):
|
||||
# rules_typescript expects a function with two or more arguments, but our
|
||||
|
@ -14,8 +14,8 @@ interface Update {
|
||||
|
||||
/**
|
||||
* Represents the header configuration options for an HTTP request.
|
||||
*
|
||||
* Instances should be assumed immutable with lazy parsing.
|
||||
* Instances are immutable. Modifying methods return a cloned
|
||||
* instance with the change. The original object is never changed.
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
@ -85,11 +85,11 @@ export class HttpHeaders {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for existence of a header by a given name.
|
||||
* Checks for existence of a given header.
|
||||
*
|
||||
* @param name The header name to check for existence.
|
||||
*
|
||||
* @returns Whether the header exits.
|
||||
* @returns True if the header exists, false otherwise.
|
||||
*/
|
||||
has(name: string): boolean {
|
||||
this.init();
|
||||
@ -98,11 +98,11 @@ export class HttpHeaders {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the first header value that matches a given name.
|
||||
* Retrieves the first value of a given header.
|
||||
*
|
||||
* @param name The header name to retrieve.
|
||||
* @param name The header name.
|
||||
*
|
||||
* @returns A string if the header exists, null otherwise
|
||||
* @returns The value string if the header exists, null otherwise
|
||||
*/
|
||||
get(name: string): string|null {
|
||||
this.init();
|
||||
@ -123,9 +123,9 @@ export class HttpHeaders {
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of header values for a given header name.
|
||||
* Retrieves a list of values for a given header.
|
||||
*
|
||||
* @param name The header name from which to retrieve the values.
|
||||
* @param name The header name from which to retrieve values.
|
||||
*
|
||||
* @returns A string of values if the header exists, null otherwise.
|
||||
*/
|
||||
@ -136,36 +136,38 @@ export class HttpHeaders {
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a new header value to the existing set of
|
||||
* header values.
|
||||
* Appends a new value to the existing set of values for a header
|
||||
* and returns them in a clone of the original instance.
|
||||
*
|
||||
* @param name The header name for which to append the values.
|
||||
* @param name The header name for which to append the value or values.
|
||||
* @param value The new value or array of values.
|
||||
*
|
||||
* @returns A clone of the HTTP header object with the value appended.
|
||||
* @returns A clone of the HTTP headers object with the value appended to the given header.
|
||||
*/
|
||||
|
||||
append(name: string, value: string|string[]): HttpHeaders {
|
||||
return this.clone({name, value, op: 'a'});
|
||||
}
|
||||
/**
|
||||
* Sets a header value for a given name. If the header name already exists,
|
||||
* its value is replaced with the given value.
|
||||
* Sets or modifies a value for a given header in a clone of the original instance.
|
||||
* If the header already exists, its value is replaced with the given value
|
||||
* in the returned object.
|
||||
*
|
||||
* @param name The header name.
|
||||
* @param value The value to set or overide for a given name.
|
||||
* @param value The value or values to set or overide for the given header.
|
||||
*
|
||||
* @returns A clone of the HTTP header object with the newly set header value.
|
||||
* @returns A clone of the HTTP headers object with the newly set header value.
|
||||
*/
|
||||
set(name: string, value: string|string[]): HttpHeaders {
|
||||
return this.clone({name, value, op: 's'});
|
||||
}
|
||||
/**
|
||||
* Deletes all header values for a given name.
|
||||
* Deletes values for a given header in a clone of the original instance.
|
||||
*
|
||||
* @param name The header name.
|
||||
* @param value The header values to delete for a given name.
|
||||
* @param value The value or values to delete for the given header.
|
||||
*
|
||||
* @returns A clone of the HTTP header object.
|
||||
* @returns A clone of the HTTP headers object with the given value deleted.
|
||||
*/
|
||||
delete (name: string, value?: string|string[]): HttpHeaders {
|
||||
return this.clone({name, value, op: 'd'});
|
||||
|
@ -12,7 +12,7 @@ import {InjectionToken} from '@angular/core';
|
||||
* A DI Token representing the main rendering context. In a browser this is the DOM Document.
|
||||
*
|
||||
* Note: Document might not be available in the Application Context when Application and Rendering
|
||||
* Contexts are not the same (e.g. when running the application into a Web Worker).
|
||||
* Contexts are not the same (e.g. when running the application in a Web Worker).
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
|
79
packages/common/upgrade/test/params.spec.ts
Normal file
79
packages/common/upgrade/test/params.spec.ts
Normal file
@ -0,0 +1,79 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. 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.io/license
|
||||
*/
|
||||
|
||||
import {AngularJSUrlCodec} from '../src/params';
|
||||
|
||||
describe('AngularJSUrlCodec', () => {
|
||||
const codec = new AngularJSUrlCodec();
|
||||
|
||||
describe('parse', () => {
|
||||
it('should parse a complex URL', () => {
|
||||
const result = codec.parse('http://server.com:1234/foo?bar=true#heading');
|
||||
expect(result.href).toBe('http://server.com:1234/foo?bar=true#heading');
|
||||
expect(result.protocol).toBe('http');
|
||||
expect(result.host).toBe('server.com:1234');
|
||||
expect(result.search).toBe('bar=true');
|
||||
expect(result.hash).toBe('heading');
|
||||
expect(result.hostname).toBe('server.com');
|
||||
expect(result.port).toBe('1234');
|
||||
expect(result.pathname).toBe('/foo');
|
||||
});
|
||||
|
||||
it('should parse a URL without search', () => {
|
||||
const result = codec.parse('http://server.com:1234/foo#heading');
|
||||
expect(result.href).toBe('http://server.com:1234/foo#heading');
|
||||
expect(result.search).toBe('');
|
||||
expect(result.hash).toBe('heading');
|
||||
expect(result.pathname).toBe('/foo');
|
||||
});
|
||||
|
||||
it('should parse a URL without hash', () => {
|
||||
const result = codec.parse('http://server.com:1234/foo?bar=true');
|
||||
expect(result.href).toBe('http://server.com:1234/foo?bar=true');
|
||||
expect(result.search).toBe('bar=true');
|
||||
expect(result.hash).toBe('');
|
||||
expect(result.pathname).toBe('/foo');
|
||||
});
|
||||
|
||||
it('should parse a basic URL', () => {
|
||||
const result = codec.parse('http://server.com');
|
||||
expect(result.href).toBe('http://server.com/');
|
||||
expect(result.protocol).toBe('http');
|
||||
expect(result.host).toBe('server.com');
|
||||
expect(result.search).toBe('');
|
||||
expect(result.hash).toBe('');
|
||||
expect(result.hostname).toBe('server.com');
|
||||
expect(result.port).toBe('');
|
||||
expect(result.pathname).toBe('/');
|
||||
});
|
||||
|
||||
it('should apply a base', () => {
|
||||
const withoutSlash = codec.parse('foo/bar', 'http://abc.xyz');
|
||||
expect(withoutSlash.href).toBe('http://abc.xyz/foo/bar');
|
||||
const withSlash = codec.parse('/foo/bar', 'http://abc.xyz/');
|
||||
expect(withSlash.href).toBe('http://abc.xyz/foo/bar');
|
||||
});
|
||||
|
||||
it('should ignore an empty base', () => {
|
||||
const result = codec.parse('http://abc.xyz', '');
|
||||
expect(result.href).toBe('http://abc.xyz/');
|
||||
});
|
||||
|
||||
it('should throw an error for an invalid URL', () => {
|
||||
expect(() => {
|
||||
codec.parse('/foo/bar');
|
||||
}).toThrowError('Invalid URL (/foo/bar) with base (undefined)');
|
||||
});
|
||||
|
||||
it('should throw an error for an invalid base', () => {
|
||||
expect(() => {
|
||||
codec.parse('http://foo.bar', 'abc');
|
||||
}).toThrowError('Invalid URL (http://foo.bar) with base (abc)');
|
||||
});
|
||||
});
|
||||
});
|
@ -62,12 +62,8 @@ import {Subject, Subscription} from 'rxjs';
|
||||
* @publicApi
|
||||
*/
|
||||
export class EventEmitter<T extends any> extends Subject<T> {
|
||||
// TODO: mark this as internal once all the facades are gone
|
||||
// we can't mark it as internal now because EventEmitter exported via @angular/core would not
|
||||
// contain this property making it incompatible with all the code that uses EventEmitter via
|
||||
// facades, which are local to the code and do not have this property stripped.
|
||||
/**
|
||||
* Internal
|
||||
* @internal
|
||||
*/
|
||||
__isAsync: boolean; // tslint:disable-line
|
||||
|
||||
|
@ -57,8 +57,38 @@ export const NG_VALIDATORS = new InjectionToken<Array<Validator|Function>>('NgVa
|
||||
export const NG_ASYNC_VALIDATORS =
|
||||
new InjectionToken<Array<Validator|Function>>('NgAsyncValidators');
|
||||
|
||||
/**
|
||||
* A regular expression that matches valid e-mail addresses.
|
||||
*
|
||||
* At a high level, this regexp matches e-mail addresses of the format `local-part@tld`, where:
|
||||
* - `local-part` consists of one or more of the allowed characters (alphanumeric and some
|
||||
* punctuation symbols).
|
||||
* - `local-part` cannot begin or end with a period (`.`).
|
||||
* - `local-part` cannot be longer than 64 characters.
|
||||
* - `tld` consists of one or more `labels` separated by periods (`.`). For example `localhost` or
|
||||
* `foo.com`.
|
||||
* - A `label` consists of one or more of the allowed characters (alphanumeric, dashes (`-`) and
|
||||
* periods (`.`)).
|
||||
* - A `label` cannot begin or end with a dash (`-`) or a period (`.`).
|
||||
* - A `label` cannot be longer than 63 characters.
|
||||
* - The whole address cannot be longer than 254 characters.
|
||||
*
|
||||
* ## Implementation background
|
||||
*
|
||||
* This regexp was ported over from AngularJS (see there for git history):
|
||||
* https://github.com/angular/angular.js/blob/c133ef836/src/ng/directive/input.js#L27
|
||||
* It is based on the
|
||||
* [WHATWG version](https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address) with
|
||||
* some enhancements to incorporate more RFC rules (such as rules related to domain names and the
|
||||
* lengths of different parts of the address). The main differences from the WHATWG version are:
|
||||
* - Disallow `local-part` to begin or end with a period (`.`).
|
||||
* - Disallow `local-part` length to exceed 64 characters.
|
||||
* - Disallow total address length to exceed 254 characters.
|
||||
*
|
||||
* See [this commit](https://github.com/angular/angular.js/commit/f3f5cf72e) for more details.
|
||||
*/
|
||||
const EMAIL_REGEXP =
|
||||
/^(?=.{1,254}$)(?=.{1,64}@)[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+(\.[-!#$%&'*+/0-9=?A-Z^_`a-z{|}~]+)*@[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?(\.[A-Za-z0-9]([A-Za-z0-9-]{0,61}[A-Za-z0-9])?)*$/;
|
||||
/^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
||||
|
||||
/**
|
||||
* @description
|
||||
@ -191,6 +221,20 @@ export class Validators {
|
||||
* @description
|
||||
* Validator that requires the control's value pass an email validation test.
|
||||
*
|
||||
* Tests the value using a [regular expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions)
|
||||
* pattern suitable for common usecases. The pattern is based on the definition of a valid email
|
||||
* address in the [WHATWG HTML specification](https://html.spec.whatwg.org/multipage/input.html#valid-e-mail-address)
|
||||
* with some enhancements to incorporate more RFC rules (such as rules related to domain names and
|
||||
* the lengths of different parts of the address).
|
||||
*
|
||||
* The differences from the WHATWG version include:
|
||||
* - Disallow `local-part` (the part before the `@` symbol) to begin or end with a period (`.`).
|
||||
* - Disallow `local-part` to be longer than 64 characters.
|
||||
* - Disallow the whole address to be longer than 254 characters.
|
||||
*
|
||||
* If this pattern does not satisfy your business needs, you can use `Validators.pattern()` to
|
||||
* validate the value against a different pattern.
|
||||
*
|
||||
* @usageNotes
|
||||
*
|
||||
* ### Validate that the field matches a valid email pattern
|
||||
|
@ -42,12 +42,12 @@ export type UrlMatchResult = {
|
||||
/**
|
||||
* A function for matching a route against URLs. Implement a custom URL matcher
|
||||
* for `Route.matcher` when a combination of `path` and `pathMatch`
|
||||
* is not expressive enough.
|
||||
* is not expressive enough. Cannot be used together with `path` and `pathMatch`.
|
||||
*
|
||||
* @param segments An array of URL segments.
|
||||
* @param group A segment group.
|
||||
* @param route The route to match against.
|
||||
* @returns The match-result,
|
||||
* @returns The match-result.
|
||||
*
|
||||
* @usageNotes
|
||||
*
|
||||
@ -386,9 +386,11 @@ export type RunGuardsAndResolvers = 'pathParamsChange' | 'pathParamsOrQueryParam
|
||||
*/
|
||||
export interface Route {
|
||||
/**
|
||||
* The path to match against, a URL string that uses router matching notation.
|
||||
* The path to match against. Cannot be used together with a custom `matcher` function.
|
||||
* A URL string that uses router matching notation.
|
||||
* Can be a wild card (`**`) that matches any URL (see Usage Notes below).
|
||||
* Default is "/" (the root path).
|
||||
*
|
||||
*/
|
||||
path?: string;
|
||||
/**
|
||||
@ -408,8 +410,7 @@ export interface Route {
|
||||
*/
|
||||
pathMatch?: string;
|
||||
/**
|
||||
* A URL-matching function to use as a custom strategy for path matching.
|
||||
* If present, supersedes `path` and `pathMatch`.
|
||||
* A custom URL-matching function. Cannot be used together with `path`.
|
||||
*/
|
||||
matcher?: UrlMatcher;
|
||||
/**
|
||||
|
@ -18,14 +18,17 @@ import {PRIMARY_OUTLET} from '../shared';
|
||||
*
|
||||
* Acts as a placeholder that Angular dynamically fills based on the current router state.
|
||||
*
|
||||
* Each outlet can have a unique name, determined by the optional `name` attribute.
|
||||
* The name cannot be set or changed dynamically. If not set, default value is "primary".
|
||||
*
|
||||
* ```
|
||||
* <router-outlet></router-outlet>
|
||||
* <router-outlet name='left'></router-outlet>
|
||||
* <router-outlet name='right'></router-outlet>
|
||||
* ```
|
||||
*
|
||||
* A router outlet will emit an activate event any time a new component is being instantiated,
|
||||
* and a deactivate event when it is being destroyed.
|
||||
* A router outlet emits an activate event when a new component is instantiated,
|
||||
* and a deactivate event when a component is destroyed.
|
||||
*
|
||||
* ```
|
||||
* <router-outlet
|
||||
|
@ -265,23 +265,20 @@ export type CanDeactivateFn<T> =
|
||||
* @description
|
||||
*
|
||||
* Interface that classes can implement to be a data provider.
|
||||
* A data provider class can be used with the router to resolve data during navigation.
|
||||
* The interface defines a `resolve()` method that will be invoked when the navigation starts.
|
||||
* The router will then wait for the data to be resolved before the route is finally activated.
|
||||
*
|
||||
* ```
|
||||
* class Backend {
|
||||
* fetchTeam(id: string) {
|
||||
* return 'someTeam';
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* @Injectable()
|
||||
* class TeamResolver implements Resolve<Team> {
|
||||
* constructor(private backend: Backend) {}
|
||||
* @Injectable({ providedIn: 'root' })
|
||||
* export class HeroResolver implements Resolve<Hero> {
|
||||
* constructor(private service: HeroService) {}
|
||||
*
|
||||
* resolve(
|
||||
* route: ActivatedRouteSnapshot,
|
||||
* state: RouterStateSnapshot
|
||||
* ): Observable<any>|Promise<any>|any {
|
||||
* return this.backend.fetchTeam(route.params.id);
|
||||
* return this.service.getHero(route.paramMap.get('id'));
|
||||
* }
|
||||
* }
|
||||
*
|
||||
@ -289,42 +286,46 @@ export type CanDeactivateFn<T> =
|
||||
* imports: [
|
||||
* RouterModule.forRoot([
|
||||
* {
|
||||
* path: 'team/:id',
|
||||
* component: TeamComponent,
|
||||
* path: 'detail/:id',
|
||||
* component: HeroDetailComponent,
|
||||
* resolve: {
|
||||
* team: TeamResolver
|
||||
* hero: HeroResolver
|
||||
* }
|
||||
* }
|
||||
* ])
|
||||
* ],
|
||||
* providers: [TeamResolver]
|
||||
* exports: [RouterModule]
|
||||
* })
|
||||
* class AppModule {}
|
||||
* export class AppRoutingModule {}
|
||||
* ```
|
||||
*
|
||||
* You can alternatively provide a function with the `resolve` signature:
|
||||
*
|
||||
* ```
|
||||
* export const myHero: Hero = {
|
||||
* // ...
|
||||
* }
|
||||
*
|
||||
* @NgModule({
|
||||
* imports: [
|
||||
* RouterModule.forRoot([
|
||||
* {
|
||||
* path: 'team/:id',
|
||||
* component: TeamComponent,
|
||||
* path: 'detail/:id',
|
||||
* component: HeroComponent,
|
||||
* resolve: {
|
||||
* team: 'teamResolver'
|
||||
* hero: 'heroResolver'
|
||||
* }
|
||||
* }
|
||||
* ])
|
||||
* ],
|
||||
* providers: [
|
||||
* {
|
||||
* provide: 'teamResolver',
|
||||
* useValue: (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => 'team'
|
||||
* provide: 'heroResolver',
|
||||
* useValue: (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => myHero
|
||||
* }
|
||||
* ]
|
||||
* })
|
||||
* class AppModule {}
|
||||
* export class AppModule {}
|
||||
* ```
|
||||
*
|
||||
* @publicApi
|
||||
|
@ -11,6 +11,7 @@ import {CacheState, UpdateCacheStatus, UpdateSource} from './api';
|
||||
import {AssetGroup, LazyAssetGroup, PrefetchAssetGroup} from './assets';
|
||||
import {DataGroup} from './data';
|
||||
import {Database} from './database';
|
||||
import {DebugHandler} from './debug';
|
||||
import {IdleScheduler} from './idle';
|
||||
import {Manifest} from './manifest';
|
||||
|
||||
@ -60,7 +61,8 @@ export class AppVersion implements UpdateSource {
|
||||
|
||||
constructor(
|
||||
private scope: ServiceWorkerGlobalScope, private adapter: Adapter, private database: Database,
|
||||
private idle: IdleScheduler, readonly manifest: Manifest, readonly manifestHash: string) {
|
||||
private idle: IdleScheduler, private debugHandler: DebugHandler, readonly manifest: Manifest,
|
||||
readonly manifestHash: string) {
|
||||
// The hashTable within the manifest is an Object - convert it to a Map for easier lookups.
|
||||
Object.keys(this.manifest.hashTable).forEach(url => {
|
||||
this.hashTable.set(url, this.manifest.hashTable[url]);
|
||||
@ -85,11 +87,12 @@ export class AppVersion implements UpdateSource {
|
||||
});
|
||||
|
||||
// Process each `DataGroup` declared in the manifest.
|
||||
this.dataGroups = (manifest.dataGroups || [])
|
||||
.map(
|
||||
config => new DataGroup(
|
||||
this.scope, this.adapter, config, this.database,
|
||||
`${adapter.cacheNamePrefix}:${config.version}:data`));
|
||||
this.dataGroups =
|
||||
(manifest.dataGroups || [])
|
||||
.map(
|
||||
config => new DataGroup(
|
||||
this.scope, this.adapter, config, this.database, this.debugHandler,
|
||||
`${adapter.cacheNamePrefix}:${config.version}:data`));
|
||||
|
||||
// This keeps backwards compatibility with app versions without navigation urls.
|
||||
// Fix: https://github.com/angular/angular/issues/27209
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
import {Adapter, Context} from './adapter';
|
||||
import {Database, Table} from './database';
|
||||
import {DebugHandler} from './debug';
|
||||
import {DataGroupConfig} from './manifest';
|
||||
|
||||
/**
|
||||
@ -243,7 +244,8 @@ export class DataGroup {
|
||||
|
||||
constructor(
|
||||
private scope: ServiceWorkerGlobalScope, private adapter: Adapter,
|
||||
private config: DataGroupConfig, private db: Database, private prefix: string) {
|
||||
private config: DataGroupConfig, private db: Database, private debugHandler: DebugHandler,
|
||||
private prefix: string) {
|
||||
this.patterns = this.config.patterns.map(pattern => new RegExp(pattern));
|
||||
this.cache = this.scope.caches.open(`${this.prefix}:dynamic:${this.config.name}:cache`);
|
||||
this.lruTable = this.db.open(`${this.prefix}:dynamic:${this.config.name}:lru`);
|
||||
@ -334,7 +336,7 @@ export class DataGroup {
|
||||
res = fromCache.res;
|
||||
// Check the age of the resource.
|
||||
if (this.config.refreshAheadMs !== undefined && fromCache.age >= this.config.refreshAheadMs) {
|
||||
ctx.waitUntil(this.safeCacheResponse(req, this.safeFetch(req)));
|
||||
ctx.waitUntil(this.safeCacheResponse(req, this.safeFetch(req), lru));
|
||||
}
|
||||
}
|
||||
|
||||
@ -353,10 +355,10 @@ export class DataGroup {
|
||||
res = this.adapter.newResponse(null, {status: 504, statusText: 'Gateway Timeout'});
|
||||
|
||||
// Cache the network response eventually.
|
||||
ctx.waitUntil(this.safeCacheResponse(req, networkFetch));
|
||||
ctx.waitUntil(this.safeCacheResponse(req, networkFetch, lru));
|
||||
} else {
|
||||
// The request completed in time, so cache it inline with the response flow.
|
||||
await this.cacheResponse(req, res, lru);
|
||||
await this.safeCacheResponse(req, res, lru);
|
||||
}
|
||||
|
||||
return res;
|
||||
@ -378,14 +380,14 @@ export class DataGroup {
|
||||
|
||||
// If the network fetch times out or errors, fall back on the cache.
|
||||
if (res === undefined) {
|
||||
ctx.waitUntil(this.safeCacheResponse(req, networkFetch, true));
|
||||
ctx.waitUntil(this.safeCacheResponse(req, networkFetch, lru, true));
|
||||
|
||||
// Ignore the age, the network response will be cached anyway due to the
|
||||
// behavior of freshness.
|
||||
const fromCache = await this.loadFromCache(req, lru);
|
||||
res = (fromCache !== null) ? fromCache.res : null;
|
||||
} else {
|
||||
await this.cacheResponse(req, res, lru, true);
|
||||
await this.safeCacheResponse(req, res, lru, true);
|
||||
}
|
||||
|
||||
// Either the network fetch didn't time out, or the cache yielded a usable response.
|
||||
@ -432,12 +434,26 @@ export class DataGroup {
|
||||
}
|
||||
}
|
||||
|
||||
private async safeCacheResponse(req: Request, res: Promise<Response>, okToCacheOpaque?: boolean):
|
||||
Promise<void> {
|
||||
private async safeCacheResponse(
|
||||
req: Request, resOrPromise: Promise<Response>|Response, lru: LruList,
|
||||
okToCacheOpaque?: boolean): Promise<void> {
|
||||
try {
|
||||
await this.cacheResponse(req, await res, await this.lru(), okToCacheOpaque);
|
||||
const res = await resOrPromise;
|
||||
try {
|
||||
await this.cacheResponse(req, res, lru, okToCacheOpaque);
|
||||
} catch (err) {
|
||||
// Saving the API response failed. This could be a result of a full storage.
|
||||
// Since this data is cached lazily and temporarily, continue serving clients as usual.
|
||||
this.debugHandler.log(
|
||||
err,
|
||||
`DataGroup(${this.config.name}@${this.config.version}).safeCacheResponse(${req.url}, status: ${res.status})`);
|
||||
|
||||
// TODO: Better detect/handle full storage; e.g. using
|
||||
// [navigator.storage](https://developer.mozilla.org/en-US/docs/Web/API/NavigatorStorage/storage).
|
||||
}
|
||||
} catch {
|
||||
// TODO: handle this error somehow?
|
||||
// Request failed
|
||||
// TODO: Handle this error somehow?
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -534,7 +534,8 @@ export class Driver implements Debuggable, UpdateSource {
|
||||
// created for it.
|
||||
if (!this.versions.has(hash)) {
|
||||
this.versions.set(
|
||||
hash, new AppVersion(this.scope, this.adapter, this.db, this.idle, manifest, hash));
|
||||
hash, new AppVersion(
|
||||
this.scope, this.adapter, this.db, this.idle, this.debugger, manifest, hash));
|
||||
}
|
||||
});
|
||||
|
||||
@ -784,7 +785,8 @@ export class Driver implements Debuggable, UpdateSource {
|
||||
}
|
||||
|
||||
private async setupUpdate(manifest: Manifest, hash: string): Promise<void> {
|
||||
const newVersion = new AppVersion(this.scope, this.adapter, this.db, this.idle, manifest, hash);
|
||||
const newVersion =
|
||||
new AppVersion(this.scope, this.adapter, this.db, this.idle, this.debugger, manifest, hash);
|
||||
|
||||
// Firstly, check if the manifest version is correct.
|
||||
if (manifest.configVersion !== SUPPORTED_CONFIG_VERSION) {
|
||||
|
@ -34,6 +34,9 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
||||
.addFile('/lazy/unchanged2.txt', 'this is unchanged (2)')
|
||||
.addUnhashedFile('/unhashed/a.txt', 'this is unhashed', {'Cache-Control': 'max-age=10'})
|
||||
.addUnhashedFile('/unhashed/b.txt', 'this is unhashed b', {'Cache-Control': 'no-cache'})
|
||||
.addUnhashedFile('/api/foo', 'this is api foo', {'Cache-Control': 'no-cache'})
|
||||
.addUnhashedFile(
|
||||
'/api-static/bar', 'this is static api bar', {'Cache-Control': 'no-cache'})
|
||||
.build();
|
||||
|
||||
const distUpdate =
|
||||
@ -140,11 +143,21 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
||||
version: 42,
|
||||
maxAge: 3600000,
|
||||
maxSize: 100,
|
||||
strategy: 'performance',
|
||||
strategy: 'freshness',
|
||||
patterns: [
|
||||
'/api/.*',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'api-static',
|
||||
version: 43,
|
||||
maxAge: 3600000,
|
||||
maxSize: 100,
|
||||
strategy: 'performance',
|
||||
patterns: [
|
||||
'/api-static/.*',
|
||||
],
|
||||
},
|
||||
],
|
||||
navigationUrls: processNavigationUrls(''),
|
||||
hashTable: tmpHashTableForFs(dist),
|
||||
@ -809,6 +822,9 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
||||
`ngsw:${baseHref}:42:data:dynamic:api:cache`,
|
||||
`ngsw:${baseHref}:db:ngsw:${baseHref}:42:data:dynamic:api:lru`,
|
||||
`ngsw:${baseHref}:db:ngsw:${baseHref}:42:data:dynamic:api:age`,
|
||||
`ngsw:${baseHref}:43:data:dynamic:api-static:cache`,
|
||||
`ngsw:${baseHref}:db:ngsw:${baseHref}:43:data:dynamic:api-static:lru`,
|
||||
`ngsw:${baseHref}:db:ngsw:${baseHref}:43:data:dynamic:api-static:age`,
|
||||
];
|
||||
|
||||
const getClientAssignments = async(sw: SwTestHarness, baseHref: string) => {
|
||||
@ -1212,6 +1228,48 @@ import {SwTestHarness, SwTestHarnessBuilder} from '../testing/scope';
|
||||
server.assertSawRequestFor('/foo.txt');
|
||||
});
|
||||
|
||||
it('keeps serving api requests with freshness strategy when failing to write to cache',
|
||||
async() => {
|
||||
// Initialize the SW.
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
server.clearRequests();
|
||||
|
||||
// Make the caches unwritable.
|
||||
spyOn(MockCache.prototype, 'put').and.throwError('Can\'t touch this');
|
||||
spyOn(driver.debugger, 'log');
|
||||
|
||||
expect(await makeRequest(scope, '/api/foo')).toEqual('this is api foo');
|
||||
expect(driver.state).toBe(DriverReadyState.NORMAL);
|
||||
// Since we are swallowing an error here, make sure it is at least properly logged
|
||||
expect(driver.debugger.log)
|
||||
.toHaveBeenCalledWith(
|
||||
new Error('Can\'t touch this'),
|
||||
'DataGroup(api@42).safeCacheResponse(/api/foo, status: 200)');
|
||||
server.assertSawRequestFor('/api/foo');
|
||||
});
|
||||
|
||||
it('keeps serving api requests with performance strategy when failing to write to cache',
|
||||
async() => {
|
||||
// Initialize the SW.
|
||||
expect(await makeRequest(scope, '/foo.txt')).toEqual('this is foo');
|
||||
await driver.initialized;
|
||||
server.clearRequests();
|
||||
|
||||
// Make the caches unwritable.
|
||||
spyOn(MockCache.prototype, 'put').and.throwError('Can\'t touch this');
|
||||
spyOn(driver.debugger, 'log');
|
||||
|
||||
expect(await makeRequest(scope, '/api-static/bar')).toEqual('this is static api bar');
|
||||
expect(driver.state).toBe(DriverReadyState.NORMAL);
|
||||
// Since we are swallowing an error here, make sure it is at least properly logged
|
||||
expect(driver.debugger.log)
|
||||
.toHaveBeenCalledWith(
|
||||
new Error('Can\'t touch this'),
|
||||
'DataGroup(api-static@43).safeCacheResponse(/api-static/bar, status: 200)');
|
||||
server.assertSawRequestFor('/api-static/bar');
|
||||
});
|
||||
|
||||
it('ignores invalid `only-if-cached` requests ', async() => {
|
||||
const requestFoo = (cache: RequestCache | 'only-if-cached', mode: RequestMode) =>
|
||||
makeRequest(scope, '/foo.txt', undefined, {cache, mode});
|
||||
|
70
scripts/local-dev/setup-rbe.sh
Executable file
70
scripts/local-dev/setup-rbe.sh
Executable file
@ -0,0 +1,70 @@
|
||||
#!/bin/bash
|
||||
# A script for automatically configuring a user's local dev
|
||||
# environment to use Remote Build Execution.
|
||||
|
||||
# Determine the root directory of the Angular github repo.
|
||||
project_directory=$(git rev-parse --show-toplevel 2> /dev/null);
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "This command must be run from within the cloned \"angular/angular\" repository.";
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
# Confirm gcloud installed and available as a command.
|
||||
if [ ! -x "$(command -v gcloud)" ]; then
|
||||
echo "gcloud command is not available. Please install gcloud before continuing.";
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
# Confirm the parameter provided to the script is a directory
|
||||
if [[ ! -d $1 ]]; then
|
||||
echo -e "Invalid command syntax.
|
||||
|
||||
\e[1mUsage:\e[0m $0 <ServiceAccountKeyLocation>
|
||||
|
||||
\e[1mExample:\e[0m ./setup-rbe ~/my_key_storage_directory/
|
||||
|
||||
The directory provided will be used to store the GCP service account key
|
||||
for the angular-local-dev service account. This key will then be used to
|
||||
authenticate for usage of the Remote Build Execution system and Remote Caching.
|
||||
";
|
||||
exit 1;
|
||||
fi
|
||||
credentials_directory=$(readlink -f $1)
|
||||
if [[ ! -d $credentials_directory ]]; then
|
||||
echo "The specified directory does not exist. Please create the directory and rerun.";
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
# Create the service account key in the provided directory.
|
||||
echo "Checking provided directory for a service account key.";
|
||||
json_key_filepath="$credentials_directory/angular-local-dev-key.json";
|
||||
if [[ -f $json_key_filepath ]]; then
|
||||
echo "Angular Local Dev key already exists, reusing this key.";
|
||||
else
|
||||
# Confirm the user is already logged into gcloud, if they aren't
|
||||
# attempt to login
|
||||
echo "Checking gcloud login state.";
|
||||
gcloud auth print-identity-token &> /dev/null;
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Not currently logged into gcloud. Starting gcloud login now.";
|
||||
gcloud auth login;
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "gcloud login failed. Aborting.";
|
||||
exit 2;
|
||||
fi
|
||||
fi
|
||||
gcloud iam service-accounts keys create $json_key_filepath \
|
||||
--iam-account angular-local-dev@internal-200822.iam.gserviceaccount.com \
|
||||
--quiet --project internal-200822;
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Downloading service account key failed. Aborting.";
|
||||
exit 2;
|
||||
fi
|
||||
fi
|
||||
|
||||
# The full path to the .bazelrc.user file
|
||||
bazelrc_user_filepath="$project_directory/.bazelrc.user";
|
||||
# Create the bazelrc.user file, echo the config flags into the file.
|
||||
touch $bazelrc_user_filepath;
|
||||
echo "build --config=remote-http-caching" >> $bazelrc_user_filepath;
|
||||
echo "build --google_credentials=$json_key_filepath" >> $bazelrc_user_filepath;
|
1
tools/public_api_guard/core/core.d.ts
vendored
1
tools/public_api_guard/core/core.d.ts
vendored
@ -326,7 +326,6 @@ export declare class ErrorHandler {
|
||||
}
|
||||
|
||||
export declare class EventEmitter<T extends any> extends Subject<T> {
|
||||
__isAsync: boolean;
|
||||
constructor(isAsync?: boolean);
|
||||
emit(value?: T): void;
|
||||
subscribe(generatorOrNext?: any, error?: any, complete?: any): Subscription;
|
||||
|
Reference in New Issue
Block a user